# Mount Points Mount points define the different components of your TelemetryOS application and where they run # Mount Points Mount points define the different components of your TelemetryOS application and where they run. ## Overview Standard TelemetryOS applications can have up to four types of components: 1. **Render** - Content displayed on devices 2. **Settings** - Configuration UI in admin portal 3. **Workers** - Background scripts that run continuously 4. **Containers** - Docker containers for backend services Each component serves a specific purpose and runs in a different context. ## Render Mount Point ### Purpose The render mount point provides the content that displays on physical devices (TVs, kiosks, digital signage). This is what end users and customers see. ### Configuration ```json { "mountPoints": { "render": "/render" } } ``` The path `/render` means the TelemetryOS platform will load your application at `https://your-app.com/render` in an iframe. ### Context * **Runs on:** Physical devices and in Studio preview * **Environment:** iframe embedded in Studio pages * **SDK Access:** Full SDK API available * **Storage Access:** All scopes (application, instance, device, shared) ### Typical Implementation ```typescript import { configure, store, playlist, media } from '@telemetryos/sdk'; configure('my-app'); // Subscribe to settings changes store().instance.subscribe('config', (newConfig) => { updateDisplay(newConfig); }); // Control playlist setTimeout(() => { playlist().nextPage(); }, 30000); // Display media const mediaItems = await media().getAllByTag('featured'); renderMedia(mediaItems); ``` ### Best Practices 1. **Subscribe to data** - Use subscriptions for real-time updates 2. **Handle no configuration** - Gracefully handle unconfigured state 3. **Optimize for performance** - Applications run 24/7 on devices 4. **Responsive design** - Support multiple screen sizes 5. **Error recovery** - Implement fallback content ### Common Use Cases * Digital signage content * Interactive kiosk interfaces * Data dashboards * Menu boards * Wayfinding displays ## Settings Mount Point ### Purpose The settings mount point provides a configuration interface that appears in the TelemetryOS admin portal. This is where administrators configure your application instances. ### Configuration ```json { "mountPoints": { "settings": "/settings" } } ``` ### Context * **Runs on:** Admin web portal (studio.telemetryos.com) * **Environment:** iframe in admin sidebar * **SDK Access:** Full SDK API available * **Storage Access:** application, instance, shared scopes (NOT device scope) **Important:** Device storage scope is not available in settings because the admin portal doesn't run on a physical device. ### Typical Implementation ```typescript import { configure, store } from '@telemetryos/sdk'; configure('my-app'); function SettingsForm() { const [city, setCity] = useState(''); // Load current settings useEffect(() => { store().instance.get('city').then(value => { if (value) setCity(value); }); }, []); // Save settings const handleSave = async () => { try { await store().instance.set('city', city); alert('Settings saved successfully!'); } catch (error) { alert('Failed to save settings'); } }; return (
); } ``` ### Best Practices 1. **Load existing values** - Show current configuration on mount 2. **Validate input** - Validate before saving to storage 3. **Provide feedback** - Show save success/failure messages 4. **Use instance scope** - Settings are typically instance-specific 5. **Keep it simple** - Admin users want quick configuration ### Communication with Render Settings and render views communicate through storage: ```typescript // Settings writes await store().instance.set('config', { city: 'New York', units: 'F' }); // Render subscribes store().instance.subscribe('config', (newConfig) => { updateDisplay(newConfig); }); ``` Changes propagate in real-time to all devices running that instance. ## Workers ### Purpose Workers are background scripts that run continuously, even when the application isn't currently visible in the playlist. They're ideal for data synchronization, monitoring, and scheduled tasks. ### Configuration ```json { "workers": [ { "script": "./workers/sync.js" }, { "script": "./workers/monitor.js" } ] } ``` Multiple workers can be defined. Each runs independently. ### Context * **Runs on:** Physical devices * **Lifecycle:** Starts when playlist containing app is loaded * **Continues:** Runs even when app not visible * **SDK Access:** Full SDK API available * **No DOM access:** Workers are JavaScript contexts, not browser contexts ### Typical Implementation **workers/sync.js:** ```javascript import { configure, store } from '@telemetryos/sdk'; configure('my-app'); async function syncData() { try { // Fetch fresh data from external API const response = await fetch('https://api.example.com/data'); const data = await response.json(); // Store for application to use await store().application.set('latest-data', data); console.log('Data synced successfully'); } catch (error) { console.error('Sync failed:', error); } // Schedule next sync setTimeout(syncData, 60000); // Every minute } // Start sync loop syncData(); ``` ### Best Practices 1. **Error handling** - Workers must be resilient to failures 2. **Periodic execution** - Use `setTimeout` or `setInterval` for scheduling 3. **Resource management** - Be mindful of CPU and memory usage 4. **Graceful degradation** - Applications should work if worker fails 5. **Logging** - Log important events for debugging ### Common Use Cases * Fetching data from external APIs * Synchronizing with inventory systems * Monitoring device health * Caching content for offline use * Background analytics ### Worker vs Render | Aspect | Render | Worker | |--------|--------|--------| | DOM Access | Yes | No | | Always Running | Only when visible | Always (when playlist loaded) | | Purpose | Display content | Background processing | | UI Updates | Direct DOM manipulation | Via storage changes | ## Containers ### Purpose Containers allow running Docker containers alongside your application on devices. They provide backend services, databases, or complex processing unavailable in browser contexts. ### Configuration ```json { "containers": [ { "name": "api-server", "image": "myapp/backend:latest", "port": 3000 }, { "name": "redis", "image": "redis:7-alpine", "port": 6379 } ] } ``` ### Context * **Runs on:** Physical devices only (not in admin portal) * **Isolation:** Each container runs independently * **Networking:** Automatic hostname routing * **Persistence:** Container data does not persist across restarts by default ### Hostname Routing Container names become hostnames automatically: ```javascript // Container name: "api-server" // Automatically available at: http://api-server fetch('http://api-server/api/data') .then(res => res.json()) .then(data => console.log(data)); ``` ### Typical Implementation **telemetry.config.json:** ```json { "name": "inventory-display", "version": "1.0.0", "mountPoints": { "render": "/render" }, "containers": [ { "name": "inventory-api", "image": "mycompany/inventory-api:v1.2", "port": 8080 } ] } ``` **Application code:** ```typescript import { configure } from '@telemetryos/sdk'; configure('inventory-display'); async function fetchInventory() { try { // Container hostname automatically resolves const response = await fetch('http://inventory-api:8080/inventory'); const inventory = await response.json(); displayInventory(inventory); } catch (error) { // Container might not be available (e.g., in admin portal) console.warn('Container not available, using fallback'); displayFallback(); } } ``` ### Best Practices 1. **Detect availability** - Containers only run on devices 2. **Provide fallbacks** - Admin portal won't have containers 3. **Use standard ports** - Avoid port conflicts 4. **Lightweight images** - Devices have limited resources 5. **Error handling** - Handle container failures gracefully 6. **Environment variables** - Use for configuration (future feature) ### Container vs Worker vs Render | Feature | Render | Worker | Container | |---------|--------|--------|-----------| | Technology | JavaScript/HTML | JavaScript | Any (Docker) | | Runs Where | Device + Admin | Device | Device Only | | Purpose | UI Display | Background JS | Backend Services | | SDK Access | Yes | Yes | No | | Network Access | Browser Fetch | Browser Fetch | Full Network | ### Common Use Cases * Backend APIs with databases * Image processing services * Machine learning inference * Message queues (Redis, RabbitMQ) * Local caching layers * Custom protocol handlers ## Mount Point Lifecycle ### Application Load Sequence 1. **Platform loads application** - Downloads and caches application files 2. **Containers start** (if defined) - Docker containers launch 3. **Workers start** (if defined) - Background scripts begin execution 4. **Render iframe created** - When page containing app becomes visible 5. **Settings iframe created** - When user opens config in admin ### Application Unload 1. **Render iframe destroyed** - When playlist moves to different page 2. **Workers continue** - Still running in background 3. **Containers continue** - Still running 4. **Full cleanup** - Only when playlist with app is unloaded ### Important Notes * **Workers survive page changes** - Keep running between playlist pages * **Render lifecycle** - Created/destroyed as playlist pages change * **Containers persist** - Run continuously while playlist is active ## Combining Mount Points ### Complete Application Example ```json { "name": "restaurant-menu", "version": "1.0.0", "mountPoints": { "render": "/render", "settings": "/settings" }, "workers": [ { "script": "./workers/price-sync.js" } ], "containers": [ { "name": "menu-api", "image": "restaurant/menu-api:latest", "port": 3000 } ] } ``` **Architecture:** * **Settings** - Admin configures restaurant location * **Render** - Displays menu with current prices * **Worker** - Syncs prices from POS system every 5 minutes * **Container** - Runs backend API with menu database ### Data Flow ``` POS System → Worker (sync) → Storage (application scope) ↓ Settings (configure) → Storage (instance scope) ↓ Render (display) ↑ Container (menu-api) → Render (fetch menu items) ``` ## Testing Mount Points Locally ### Using tos serve ```bash tos serve ``` Access mount points: * **Render:** http://localhost:3000/render * **Settings:** http://localhost:3000/settings ### Simulating Storage The development environment provides mock storage that persists in browser localStorage: ```typescript // Works in local development await store().instance.set('key', 'value'); const value = await store().instance.get('key'); ``` ### Limitations in Development * Containers don't run locally (require device environment) * Workers run in same browser context (not isolated) * Some device-specific APIs return mock data ## Next Steps * **[Configuration](./configuration.md)** - Complete telemetry.config.json reference * **[Storage Methods](../sdk-methods/storage-methods.md)** - Learn storage scopes in detail * **[Code Examples](../Development/code-examples.md)** - See complete working applications