Split the monolithic MODULE__AE_Events_PressMgmt_Launcher.md into focused, granular modules to improve maintainability and onsite utility. - Created MODULE__AE_Events_Presentation_Management.md (Back-office focus) - Created MODULE__AE_Events_Launcher.md (Podium display focus) - Created GUIDE__AE_Events_Onsite_Runbook.md (SRR and onsite workflows) - Promoted PROJECT__AE_Events_Launcher_Native_integration.md to MODULE__AE_Events_Launcher_Native.md (Permanent technical reference) - Renamed 'Press Mgmt' references to 'Presentation Management' for clarity. - Removed redundant README.md in launcher route. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
17 KiB
Aether Events — Launcher: Native Integration
Status: Operational / Permanent Reference Last Updated: 2026-05-21 (Reorganized) Primary Platform: macOS (Darwin) Fallback Platform: Linux / Windows
1. Overview
The Aether Events Launcher utilizes an Electron-based "Native Shell" to provide OS-level capabilities that are normally restricted by browser sandboxing. This enables persistent file caching, direct control of presentation software (Keynote, PowerPoint), and hardware telemetry.
Operational Modes
| Mode | Purpose | File Handling |
|---|---|---|
| Default | Standard web browser access. | Direct downloads; no local caching. |
| Onsite | Web access on event networks. | Faster polling; browser-based file management. |
| Native | Dedicated Podium Kiosk (Electron). | Full background pre-caching; atomic safe-handover. |
2. Architecture: The Three-Layer Bridge
The integration is built on a decoupled three-layer communication model to ensure security and cross-platform flexibility.
2.1 Layer 1: The Engine (Main Process)
- Repo:
~/OSIT_dev/aether_app_native_electron/(separate git repo) - File:
aether_app_native_electron/src/main/*.ts - Role: Performs the heavy lifting (Filesystem, Shell, AppleScript).
- Responsibilities:
- Managing the Hashed Cache directory.
- Executing
osascriptintents for presentation control. - Spawn/Kill process management.
2.2 Layer 2: The Gatekeeper (Preload Script)
- Namespace:
window.aetherNative - Role: Securely exposes whitelisted IPC channels to the Renderer.
- Standards: Uses
contextBridge.exposeInMainWorldto prevent arbitrary code execution.
2.3 Layer 3: The Messenger (SvelteKit Relay)
- File:
src/lib/electron/electron_relay.ts - Role: Provides a clean, typed API for Svelte components.
- Responsibilities:
- Mapping
camelCaseUI triggers tosnake_caseIPC calls. - Resolving an extension alias to a canonical Launch Profile, then to a single
native_templatestring before crossing IPC.
- Mapping
The reason for this split is simple: Launch Profiles are policy, while Native Templates are executable strings. Keeping that distinction explicit prevents the bridge from mixing config objects with runtime commands.
3. The "Zero-Config" Lifecycle
To support rapid onsite deployment, the native app requires zero manual setup.
- Seed: On launch, the Main process reads a local
seed.json(Device ID + API Key). - Identity: Calls
GET /v3/crud/event_device/{id}to pull device config and extractapp_base_url(the event FQDN) andaccount_id. - Site Context: POSTs to
/v3/crud/site_domain/search?limit=1with the FQDN to resolve the correct site. No JWT — auth isx-aether-api-key+x-account-idthroughout. - Launch: Navigates the SvelteKit frontend directly to the assigned Event Launcher route (
/events/{eventId}/launcher/{locationId}).
4. Podium Reliability Protocol
The system is designed to ensure that a presentation never fails due to network instability.
4.1 Hashed Cache Pattern
Files are stored persistently using their SHA-256 hash to prevent filename collisions and handle versioning.
- Root:
~/Library/Caches/OSIT/file_cache/ - Subdirectory: First 2 characters of hash (e.g.,
ab/) - Filename:
{hash}.file
4.2 Background Sync (File Warming)
When a user navigates to a session in the Launcher UI, the LauncherBackgroundSync component warms the cache for that specific session. To ensure full room readiness, a Force Sync Location trigger is available in the configuration UI.
- Metadata Fetch: The system fetches all sessions, presentations, and presenters for the current location into the local database (Dexie).
- Chronological Priority: Missing files are added to the download queue and sorted to prioritize the event schedule:
- Tier 1: Global Assets — Event and Location level files (virtual time 0).
- Tier 2: Session Schedule — Earliest sessions are prioritized first.
- Tier 3: Presentation Order — Within a session, speakers are prioritized by their start time.
- Tier 4: Integrity & Fairness — Tie-breakers use
created_on(oldest first) to ensure on-time uploads are cached before last-minute revisions.
- Download: Triggers background downloads via
aetherNative.download_to_cachesequentially to preserve network bandwidth and ensure file integrity.
4.3 Safe Handover (Launch Sequence)
When a user clicks "Open", the system follows a non-destructive sequence:
- Verify: Confirm hash exists in the permanent cache.
- Copy: Create an atomic copy in the system
[tmp]directory. - Restore: Rename the copy to its original filename (e.g.,
Abstract_101.pptx). - Execute: Launch the file via the OS.
5. Automation & Actuators (Phase 5)
The native shell provides specialized handlers for controlling the "Podium Experience."
5.1 Presentation Acts
| Action | Handler | Actuator (macOS) |
|---|---|---|
| Launch | launch_presentation |
open or osascript (slideshow start) |
| Control | control_presentation |
osascript (next/prev slide) |
| Clean Up | kill_processes |
killall -INT (graceful exit) |
5.2 System Management
- Telemetry: Pushes
cpu_usage,memory_free_gb, andforeground_appvia heartbeats using theget_device_inforelay. - Self-Update (Roadmap): Plan to monitor Syncthing
admin_sharefor newer.appversions and perform atomic swaps.
5.3 Implemented Actuators (Phase 5 Complete)
- Recording:
manage_recording({action})— Aperture session capture (start,stop,status). macOS only. - Display Layouts:
set_display_layout({mode, configStr?})— Mirror / Extend displays. macOS only. Primary: nativedisplay_controlbinary (resources/bin/display_control) uses CoreGraphics APIs directly — no Homebrew dependency. Built fromscripts/display_control.mviascripts/build-display-control.shon a Mac; commit the binary to the repo. Fallback:displayplacer(brew install displayplacer) used when binary is absent orconfigStroverride is set. Failures are logged to the Electron console but do not block file open. A Display Mode toggle (Extend / Mirror) is available in the Launcher config — Native OS section, visible without Technical Mode. - Power Control:
power_control({action})— Shutdown, reboot, sleep. macOS + Linux. - Window Control:
window_control({action})— Maximize, minimize, fullscreen, kiosk mode. - Wallpaper:
set_wallpaper({path?, url?, url_external?, display?, api_key?, account_id?})— Downloads from URL (cached locally) or applies a local path. Per-display targeting ('all'/'primary'/'external'). macOS only in production; Linux returns a dev-mode preview payload.
Note:
update_appis implemented as a stub — downloads but does not install. Not yet functional for end users.
6. Launcher Configuration & Management
The Launcher features a standardized, responsive configuration interface designed for onsite technical management.
6.1 UI Architecture
- Tabbed Navigation: Categorized into System, Sync, and General settings.
- Section Wrapper (
Launcher_Cfg_Section): A shared component providing a consistent header, icon, and responsive grid container.
6.2 3-Way State Logic
To manage screen real estate on varying laptop resolutions, all configuration sections utilize a 3-way visibility state:
collapsed: Content is hidden.auto: Expanded by default, but automatically closes if another "auto" section is opened.pinned: Expanded and remains open regardless of other section interactions.
6.3 Technical Mode (edit_mode)
The UI dynamically filters fields based on the user's focus. Enabling Technical Mode ($ae_loc.edit_mode) reveals advanced diagnostic and writeable fields.
| Category | Standard View (Read-Only) | Technical Mode (Read/Write) |
|---|---|---|
| Health | Heartbeat, RAM Usage, Sync Stats | Hostname, IP List, Raw Device JSON |
| OS Bridge | Folder Buttons, Recording Toggle | Manual Terminal Commands, Reset Wallpaper |
| Sync | Sync Completion Status | Millisecond Timers, Cache Prefix Logic |
| Update | Current Version Status | Manual Update Paths, URL Overrides |
7. Implementation Reference (IPC Whitelist)
All functions below are exported from src/lib/electron/electron_relay.ts and safely
no-op when window.aetherNative is not present (i.e., in browser/non-native mode).
Config & Info
get_device_config()— Returns hydrated device settings injected by the native shell on startup.get_device_info()— Returns OS metadata, IP list, hostname, and path placeholders ([home],[tmp]).
File Cache
check_cache({cache_root, hash, hash_prefix_length?, verify_hash?})— Verifies a file exists in the local hashed cache.verify_hash: truere-hashes to confirm integrity.download_to_cache({url, cache_root, hash, api_key, account_id, hash_prefix_length?})— Streams a file download to the hashed cache with SHA-256 integrity check. Stale.tmpfiles (older than 5 min) from crashed downloads are cleaned up automatically on each call.copy_from_cache_to_temp({cache_root, hash, temp_root, filename, hash_prefix_length?})— Preferred primitive. Copies a cached file to temp and returns{ success, path }. The Svelte caller decides what to do next (run a script, open it, etc.).launch_from_cache({cache_root, hash, temp_root, filename, hash_prefix_length?, native_template?})— Combines copy + launch in one call. Executes the providednative_templatestring after the file is copied to temp. If no template is supplied, treat it as an error and do not rely on Electron-side defaults.
hash_prefix_lengthdefaults to2throughout. Do not change without coordinating all devices — mismatched values create orphaned cache subdirectories.
Shell & OS
open_folder(path)— Opens a path in the OS file manager.run_cmd({cmd, timeout?, return_stdout?})— Async shell command execution.run_cmd_sync({cmd, return_stdout?})— Synchronous shell command execution.run_osascript(script)— Executes an AppleScript string. macOS only. Hardened (2026-05-11): writes script to a temp.scptfile; multi-line scripts and paths with special characters now work correctly. No shell escaping needed in the passed string.kill_processes({process_name_li})— Terminates processes by name. macOS/Linux:pkill -f. Windows:taskkill /F.open_local_file_v2(path)— Opens a file with its default OS application.
Presentations (Phase 5)
launch_presentation({path, app?, os?})— Platform-aware launcher. macOS: PowerPoint/Keynote via AppleScript. Linux: LibreOffice Impress. Resolves[home]/[tmp]placeholders.control_presentation({app, action})— Slide navigation (next/prev/start/stop) for PowerPoint or Keynote via AppleScript.
System Management (Phase 5)
set_wallpaper({path?, url?, url_external?, display?, api_key?, account_id?})— Sets desktop wallpaper. Downloads fromurl(cached to~/Library/Caches/OSIT/wallpaper/) or applies a localpath.url_externaltargets the projector/second display separately. macOS only in production; Linux returns a dev-mode preview payload without applying.window_control({action, value?})— Electron window management: maximize, minimize, fullscreen, kiosk.set_display_layout({mode, configStr?})— Mirror or extend displays viadisplayplacer. macOS only. Auto-detects viadisplayplacer list;configStroverrides auto-detection when set. Binary lookup order: bundledresources/bin/displayplacer→/opt/homebrew/bin/(Apple Silicon) →/usr/local/bin/(Intel). Requiresbrew install displayplaceron each venue Mac if not bundled.power_control({action})— Shutdown, reboot, or sleep the host machine. macOS + Linux.manage_recording({action, options?})— Aperture capture control (start/stop/status). macOS only.open_external({url, app?})— Opens a URL in Chrome, Firefox, or the default browser.update_app(args)— Stub only. Downloads but does not install. Not yet functional.list_tools()— Returns a self-documenting manifest of all available native bridge functions.
Path Placeholders
All paths passed to native handlers should use tokens rather than hardcoded OS paths:
[home]— Resolved to the user's home directory by the native bridge.[tmp]— Resolved to the system temporary directory.
8. Launch Profiles and Native Templates (No-Rebuild File Handling)
This launcher uses two related concepts:
- Launch Profile: the Svelte-side config object keyed by file extension. A profile decides which app to use, whether to extend or mirror displays, whether to use an explicit open command, whether to run post-open automation, and how long to wait before running it.
- Native Template: the single AppleScript or shell command string handed to Electron after Svelte resolves the profile. This is what Electron actually executes.
The Svelte launcher resolves a profile and then passes a native template string to
launch_from_cache. Electron only executes the template it receives. If Svelte has not
resolved a template yet, it should stop before IPC and surface a missing-profile error.
This keeps all fallback logic in Svelte, where it can be edited without rebuilding Electron.
The native layer should not invent or guess a default launch path.
The built-in defaults are organized as canonical profile names plus extension aliases. That
lets multiple file types share one profile without repeating the same app/script details.
The profile object also carries post_delay_ms, and a device-specific per-profile
launch_profiles[profile].post_delay_ms override can tune the delay without changing the bridge
contract. URL-based presentations remain a special pseudo-extension handled separately from
the cache open flow.
Native Template Formats
| Format | Example |
|---|---|
| AppleScript (macOS) | Multi-line AppleScript string with {{path}} placeholder |
| Shell command | String prefixed with shell: — e.g. shell:open "{{path}}" |
The placeholder {{path}} is replaced with the full resolved path to the file in the temp
directory after the atomic copy from cache.
Where to Configure
Launch profiles are resolved in priority order by get_launch_profile() in
launcher_file_cont.svelte:
event_device.data_json.launch_profiles— API-driven, per-device. Highest priority. Set via theevent_devicerecord (Pres Mgmt → Device Management or direct DB edit).$events_loc.launcher.launch_profiles— Local persistent config. Editable via the Launcher config UI (planned) or directlocalStoragemanipulation.
If neither is set, the resolved native template is null and the launcher should not call
Electron until an explicit template is available.
Why: this avoids a second hidden source of truth. The profile map can evolve independently of the executable string, and Electron stays a thin executor rather than a policy engine.
Key Format
Keys are lowercase file extensions without the dot. A "default" key catches all
unrecognised extensions.
The JSON below illustrates the native_template emitted after profile resolution, not the
full Launch Profile object schema.
// event_device.data_json.launch_profiles example
{
"launch_profiles": {
"pptx": "tell application \"Microsoft PowerPoint\"\n activate\n open (POSIX file \"{{path}}\")\n delay 3\nend tell\ntell application \"System Events\"\n keystroke return using command down\nend tell",
"key": "tell application \"Keynote\"\n activate\n open (POSIX file \"{{path}}\")\n delay 1\n start (front document)\nend tell",
"pdf": "shell:open \"{{path}}\"",
"default": "shell:open \"{{path}}\""
}
}
AppleScript Execution — All Handlers Hardened (2026-05-11)
All AppleScript execution in the native shell now writes scripts to a temp .scpt file and
runs osascript "<path>" rather than the old osascript -e "<inline>" approach.
run_osascript— hardened (2026-05-11, earlier batch)launch_from_cache— hardened (same batch)launch_presentation— hardened (2026-05-11, follow-up fix; was the last handler still using-e)control_presentation— uses single-line scripts with no path interpolation;-eis safe here and retained for simplicity
The -e approach breaks on (1) multi-line scripts and (2) file paths containing spaces,
quotes, or parentheses — common in conference presentation filenames.
Not Exposed via Relay (intentional)
get_seed_config/get_jwt— Exposed in the preload but not relayed to the UI. The JWT and seed are injected into the environment at startup; components should not call these directly.