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:
- Render - Content displayed on devices
- Settings - Configuration UI in admin portal
- Workers - Background scripts that run continuously
- 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
{
"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
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
- Subscribe to data - Use subscriptions for real-time updates
- Handle no configuration - Gracefully handle unconfigured state
- Optimize for performance - Applications run 24/7 on devices
- Responsive design - Support multiple screen sizes
- 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
{
"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
import { configure, store } from '@telemetryos/sdk';
configure('my-app');
function SettingsForm() {
const [city, setCity] = useState('');
// Load current settings
useEffect(() => {
store().instance.get<string>('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 (
<form>
<label>
City:
<input
value={city}
onChange={(e) => setCity(e.target.value)}
/>
</label>
<button onClick={handleSave}>Save</button>
</form>
);
}Best Practices
- Load existing values - Show current configuration on mount
- Validate input - Validate before saving to storage
- Provide feedback - Show save success/failure messages
- Use instance scope - Settings are typically instance-specific
- Keep it simple - Admin users want quick configuration
Communication with Render
Settings and render views communicate through storage:
// 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
{
"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:
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
- Error handling - Workers must be resilient to failures
- Periodic execution - Use
setTimeoutorsetIntervalfor scheduling - Resource management - Be mindful of CPU and memory usage
- Graceful degradation - Applications should work if worker fails
- 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
{
"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:
// 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:
{
"name": "inventory-display",
"version": "1.0.0",
"mountPoints": {
"render": "/render"
},
"containers": [
{
"name": "inventory-api",
"image": "mycompany/inventory-api:v1.2",
"port": 8080
}
]
}Application code:
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
- Detect availability - Containers only run on devices
- Provide fallbacks - Admin portal won't have containers
- Use standard ports - Avoid port conflicts
- Lightweight images - Devices have limited resources
- Error handling - Handle container failures gracefully
- 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
- Platform loads application - Downloads and caches application files
- Containers start (if defined) - Docker containers launch
- Workers start (if defined) - Background scripts begin execution
- Render iframe created - When page containing app becomes visible
- Settings iframe created - When user opens config in admin
Application Unload
- Render iframe destroyed - When playlist moves to different page
- Workers continue - Still running in background
- Containers continue - Still running
- 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
{
"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
tos serveAccess 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:
// 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 - Complete telemetry.config.json reference
- Storage Methods - Learn storage scopes in detail
- Code Examples - See complete working applications
Updated 5 days ago