256 lines
8.8 KiB
Markdown
256 lines
8.8 KiB
Markdown
|
|
# @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/<device-type>/<version>/manifest.json
|
||
|
|
https://updates.serve.zone/baseos/<device-type>/<version>/checksums.txt
|
||
|
|
https://updates.serve.zone/baseos/<device-type>/<version>/license-bundle.tar.gz
|
||
|
|
registry.serve.zone/baseos/hostapp/<device-type>:<version>
|
||
|
|
registry.serve.zone/baseos/baserunner:<version>
|
||
|
|
```
|
||
|
|
|
||
|
|
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.
|