# @serve.zone/baseos BaseOS is the serve.zone runtime layer for devices that should run on balenaOS-derived host systems but enroll only in Cloudly. The first implementation target is intentionally small: a `baserunner` container runs on the device, talks to the local Balena Supervisor API when available, reports status to Cloudly, and prepares the update path for Onebox and other BaseOS services. ## Goals - Use balenaOS as the proven host foundation for embedded and edge devices. - Avoid BalenaCloud enrollment for serve.zone devices. - Enroll devices directly in Cloudly with a Cloudly join token. - Let Cloudly track BaseOS nodes, desired releases, rollout state, and update health. - Let Onebox detect when it is running on BaseOS and delegate self-updates to BaseRunner. - Keep the first version useful without pretending host OS updates are solved. ## Non-Goals For V1 - No BalenaCloud fleet enrollment. - No openBalena dependency. - No full Balena API replacement. - No remote host OS updates until BaseOS has its own signed artifact channel. - No silent use of Balena trademarks as serve.zone branding. ## Runtime Model ```text balenaOS-derived host Balena Supervisor baserunner container - Cloudly enrollment and heartbeat - local Supervisor API access - local BaseOS runtime API for Onebox, later onebox container, later - detects BaseOS through BaseRunner - delegates self-update to BaseRunner - runs Onebox-managed apps in its own runtime model ``` The current scaffold implements `baserunner`; the matching Cloudly registration and heartbeat handlers live in the `cloudly` repo. Onebox integration is still a later focused change. ## Levels ### Level 1: App-Layer Updates Level 1 is the first production target. BaseOS devices boot a balenaOS-derived image with `baserunner` preloaded. `baserunner` enrolls the node in Cloudly, reports Balena Supervisor/device state, and can ask the local Supervisor to check/apply application release updates. Cloudly responsibilities: - Store BaseOS node records. - Issue and validate join tokens. - Accept BaseRunner heartbeats. - Track current BaseRunner/Onebox release metadata. - Publish desired app-layer release state. BaseRunner responsibilities: - Persist a node ID and node token in a named volume. - Report supervisor availability, OS version, supervisor version, release hash, update state, and IP information. - Receive desired app-layer state from Cloudly heartbeats. - Call Supervisor API endpoints such as `/ping`, `/v1/device`, `/v2/state/status`, and `/v1/update`. Onebox responsibilities: - Detect BaseOS via a local BaseRunner API and explicit environment such as `SERVEZONE_RUNTIME=baseos`. - Disable direct self-replacement when running on BaseOS. - Ask BaseRunner to perform BaseOS-safe app-layer updates. Level 1 does not update the host OS remotely. Host OS updates are manual or deferred. ### Level 2: Cloudly-Managed Host OS Releases Level 2 adds a Cloudly-owned BaseOS update channel. Cloudly or a central serve.zone update service hosts signed BaseOS host OS artifacts. Devices are still enrolled only in Cloudly. Artifact layout target: ```text https://updates.serve.zone/baseos///manifest.json https://updates.serve.zone/baseos///checksums.txt https://updates.serve.zone/baseos///license-bundle.tar.gz registry.serve.zone/baseos/hostapp/: registry.serve.zone/baseos/baserunner: ``` Cloudly responsibilities: - Maintain approved BaseOS channels. - Pin desired host OS versions per node or fleet. - Verify release signatures before rollout. - Track staged, applying, succeeded, failed, and rollback-needed states. BaseRunner responsibilities: - Verify release manifests and signatures. - Hold update locks during critical work. - Coordinate with the host update mechanism where available. - Report detailed progress and failure logs. This level requires careful work against balenaOS host update internals and licensing obligations for redistributed images. ### Level 3: Cloudly As A Balena-Compatible Backend Level 3 is the largest option and should only happen if Level 2 is insufficient. Cloudly would implement enough target-state and artifact APIs for a balenaOS-derived Supervisor to treat Cloudly as its backend. The device `config.json` would point to Cloudly-controlled endpoints instead of BalenaCloud endpoints. Target endpoints would include equivalents for: - API target state. - Registry pulls. - Delta service, if implemented. - VPN or remote access, if required. - Device diagnostics and logs, if required. This level is effectively a focused openBalena-like backend inside serve.zone. It is not a first milestone. ## Cloudly-Only Enrollment BaseOS devices should not need BalenaCloud identity. Enrollment flow: ```text 1. User creates a BaseOS join token in Cloudly. 2. BaseOS image is flashed with BASEOS_CLOUDLY_URL and BASEOS_JOIN_TOKEN. 3. BaseRunner boots and registers against Cloudly. 4. Cloudly returns a node token. 5. BaseRunner stores the node token in /data/baseos/state.json. 6. Future heartbeats use the node token. ``` Current provisional HTTP endpoints used by this scaffold: - `POST /baseos/v1/nodes/register` - `POST /baseos/v1/nodes/heartbeat` Cloudly implements these paths through its BaseOS manager. A deployed Cloudly instance must also have `baseosJoinToken` configured before new nodes can enroll. ## Supervisor API Usage BaseRunner uses Balena Supervisor APIs only when `BALENA_SUPERVISOR_ADDRESS` and `BALENA_SUPERVISOR_API_KEY` are provided by the `io.balena.features.supervisor-api` service label. Current calls: - `GET /ping` - `GET /v1/device?apikey=...` - `GET /v2/state/status?apikey=...` - `GET /v2/version?apikey=...` - `POST /v1/update?apikey=...` - `POST /v1/reboot?apikey=...` The `docker-compose.yml` enables the supervisor API label for `baserunner`. ## Onebox Integration Plan Onebox should detect BaseOS through explicit runtime signals, not by guessing from Balena environment variables alone. Detection signals: - `SERVEZONE_RUNTIME=baseos` - `BASEOS_AGENT_URL` or equivalent local BaseRunner API URL - successful BaseRunner handshake Onebox behavior on BaseOS: - Show BaseOS runtime in system status. - Route self-update requests to BaseRunner. - Avoid replacing its own container directly. - Use update locks around backup/restore and migration operations. ## Security - Join tokens must be one-time or short-lived. - Node tokens must be long-lived machine tokens scoped to one BaseOS node. - Host OS release manifests must be signed before Level 2. - BaseRunner must reject unsigned or untrusted desired host OS updates. - Supervisor API access is powerful; keep BaseRunner's local API private and minimal. - Never log join tokens, node tokens, or Supervisor API keys. ## Licensing And Branding balenaOS source components are open source, but trademarks are separate from copyright licenses. BaseOS must: - Preserve required upstream notices and license bundles. - Provide source/license compliance for redistributed GPL/LGPL/Yocto components. - Avoid presenting BaseOS as official Balena software. - Replace public-facing branding when redistributing images. - Use Balena names only where needed to describe compatibility or documented APIs. ## Implementation Milestones ### Milestone 1: BaseRunner Scaffold - Deno CLI and daemon structure. - Supervisor API client. - Persistent node state. - Provisional Cloudly register/heartbeat client. - Balena compose service definition. ### Milestone 2: Shared Interfaces - Add BaseOS node, runtime status, desired state, heartbeat, and registration contracts to `@serve.zone/interfaces`. - Add machine role or scoped token model for BaseOS nodes. ### Milestone 3: Cloudly BaseOS Manager - Join token validation through `baseosJoinToken`. - Node registration. - Heartbeat ingestion. - Desired state storage. - Admin/UI status views. ### Milestone 4: Onebox Runtime Detection - Add BaseOS runtime manager to Onebox. - Add status display. - Route Onebox self-update through BaseRunner when running on BaseOS. ### Milestone 5: App Release Updates - Build and publish BaseRunner release images. - Add Onebox service image/release metadata. - Let Cloudly request app-layer updates. - Report update status and failures. ### Milestone 6: Host OS Release Channel - Build BaseOS host OS images. - Publish signed manifests and artifacts. - Add Cloudly release approval and rollout controls. - Add BaseRunner host update execution and reporting. ## Current Commands ```bash deno task check deno task start deno task dev deno task fmt deno task lint ``` Run a one-shot status check: ```bash deno run --allow-env --allow-net --allow-read=/data/baseos,. --allow-write=/data/baseos mod.ts status ``` Inside balenaOS, the `baserunner` service should be started from `docker-compose.yml` so the Supervisor API environment variables are injected.