docs: refresh readme and legal info
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Task Venture Capital GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Lossless GmbH
|
||||
Copyright (c) 2026 Task Venture Capital GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
"iot",
|
||||
"edge"
|
||||
],
|
||||
"author": "Lossless GmbH",
|
||||
"author": "Task Venture Capital GmbH",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
# @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.
|
||||
BaseOS is the Cloudly-enrolled runtime layer for balenaOS-derived devices in the serve.zone ecosystem. The current package provides `BaseRunner`, a small Deno service/container that stores device identity, reports runtime status to Cloudly, and talks to the local Balena Supervisor API when that API is exposed.
|
||||
|
||||
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.
|
||||
## Issue Reporting and Security
|
||||
|
||||
## Goals
|
||||
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
||||
|
||||
- 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.
|
||||
## What Exists Today
|
||||
|
||||
## Non-Goals For V1
|
||||
BaseOS is not a full operating system distribution in this repository. It is the first app-layer runtime component for a future Cloudly-managed BaseOS device flow.
|
||||
|
||||
- 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.
|
||||
Implemented now:
|
||||
|
||||
- `BaseRunner` loads configuration from environment variables.
|
||||
- It persists `nodeId` and `nodeToken` state in JSON.
|
||||
- It registers with Cloudly through provisional BaseOS HTTP endpoints.
|
||||
- It sends periodic heartbeats with runtime and Supervisor status.
|
||||
- It checks Balena Supervisor availability through `/ping`.
|
||||
- It reads Supervisor device and target-state status when credentials are available.
|
||||
- It can request an app-layer update through the Supervisor `/v1/update` endpoint.
|
||||
- It ships a Dockerfile and `docker-compose.yml` for running `baserunner` as a balenaOS service.
|
||||
|
||||
Explicit non-goals for the current package:
|
||||
|
||||
- It does not enroll devices in BalenaCloud.
|
||||
- It does not replace openBalena.
|
||||
- It does not implement a Balena-compatible backend.
|
||||
- It does not perform host OS updates.
|
||||
- It does not provide a full image build pipeline by itself.
|
||||
|
||||
## Runtime Model
|
||||
|
||||
@@ -27,229 +35,160 @@ The first implementation target is intentionally small: a `baserunner` container
|
||||
balenaOS-derived host
|
||||
Balena Supervisor
|
||||
baserunner container
|
||||
- Cloudly enrollment and heartbeat
|
||||
- local Supervisor API access
|
||||
- local BaseOS runtime API for Onebox, later
|
||||
- persistent BaseOS node state
|
||||
- Cloudly registration and heartbeat
|
||||
- Supervisor status reads
|
||||
- Supervisor app update requests
|
||||
|
||||
onebox container, later
|
||||
- detects BaseOS through BaseRunner
|
||||
- delegates self-update to BaseRunner
|
||||
- runs Onebox-managed apps in its own runtime model
|
||||
Cloudly
|
||||
/baseos/v1/nodes/register
|
||||
/baseos/v1/nodes/heartbeat
|
||||
```
|
||||
|
||||
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.
|
||||
The matching Cloudly handlers live in the `cloudly` repository. A Cloudly deployment must be configured with a valid BaseOS join token before new devices can enroll.
|
||||
|
||||
## Levels
|
||||
## Configuration
|
||||
|
||||
### Level 1: App-Layer Updates
|
||||
`BaseRunner.fromEnv()` reads these environment variables:
|
||||
|
||||
Level 1 is the first production target.
|
||||
| Variable | Purpose |
|
||||
| --- | --- |
|
||||
| `BASEOS_CLOUDLY_URL` | Cloudly base URL. Without it, Cloudly sync is reported as `not-configured`. |
|
||||
| `BASEOS_JOIN_TOKEN` | Initial enrollment token used before a node token exists. |
|
||||
| `BASEOS_NODE_TOKEN` | Existing machine token for heartbeats. Usually persisted after registration. |
|
||||
| `BASEOS_NODE_ID` | Optional fixed node ID. A UUID is generated if missing. |
|
||||
| `BASEOS_STATE_PATH` | State file path. Defaults to `/data/baseos/state.json`. |
|
||||
| `BASEOS_HEARTBEAT_INTERVAL_MS` | Heartbeat interval. Defaults to `60000`. |
|
||||
| `BALENA_SUPERVISOR_ADDRESS` | Supervisor API base URL injected by balenaOS when enabled. |
|
||||
| `BALENA_SUPERVISOR_API_KEY` | Supervisor API key injected by balenaOS when enabled. |
|
||||
|
||||
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.
|
||||
The provided compose file enables Supervisor API access with the `io.balena.features.supervisor-api: '1'` label and stores state in the `baseos-data` volume.
|
||||
|
||||
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
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
deno task check
|
||||
deno task start
|
||||
deno task dev
|
||||
deno task fmt
|
||||
deno task lint
|
||||
deno run --allow-env --allow-net --allow-read=/data/baseos,. --allow-write=/data/baseos mod.ts <command>
|
||||
```
|
||||
|
||||
Run a one-shot status check:
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `start` | Start BaseRunner and keep the process alive. This is the default command. |
|
||||
| `status` | Print runtime, Supervisor, and Cloudly status as JSON. |
|
||||
| `check-supervisor` | Print Supervisor API configuration and availability. |
|
||||
| `request-update [--force]` | Ask the Balena Supervisor to check/apply app release updates. |
|
||||
| `help` | Print command help. |
|
||||
|
||||
Example 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.
|
||||
Run the container shape used by balenaOS:
|
||||
|
||||
```bash
|
||||
docker compose up --build baserunner
|
||||
```
|
||||
|
||||
## Cloudly Enrollment Flow
|
||||
|
||||
```text
|
||||
1. Cloudly operator creates or configures a BaseOS join token.
|
||||
2. Device boots with BASEOS_CLOUDLY_URL and BASEOS_JOIN_TOKEN.
|
||||
3. BaseRunner creates or loads local state.
|
||||
4. BaseRunner POSTs runtime status to /baseos/v1/nodes/register.
|
||||
5. Cloudly returns a node token when registration is accepted.
|
||||
6. BaseRunner saves the node token and uses it for future heartbeats.
|
||||
7. BaseRunner POSTs runtime status to /baseos/v1/nodes/heartbeat on each interval.
|
||||
```
|
||||
|
||||
Registration payloads contain the current runtime status object. Heartbeat responses can include desired state, but this package currently records connectivity and does not yet perform host-level rollout control.
|
||||
|
||||
## Supervisor API Usage
|
||||
|
||||
Supervisor calls are made only when `BALENA_SUPERVISOR_ADDRESS` is configured. Calls that require authorization also require `BALENA_SUPERVISOR_API_KEY`.
|
||||
|
||||
Current endpoints:
|
||||
|
||||
| Endpoint | Purpose |
|
||||
| --- | --- |
|
||||
| `GET /ping` | Availability check. |
|
||||
| `GET /v1/device?apikey=...` | Device state. |
|
||||
| `GET /v2/state/status?apikey=...` | App target-state/status. |
|
||||
| `GET /v2/version?apikey=...` | Supervisor version. |
|
||||
| `POST /v1/update?apikey=...` | Request app release update. |
|
||||
| `POST /v1/reboot?apikey=...` | Request reboot. |
|
||||
|
||||
## Programmatic Usage
|
||||
|
||||
```typescript
|
||||
import { BaseRunner, SupervisorClient, CloudlyConnector } from './mod.ts';
|
||||
|
||||
const runner = BaseRunner.fromEnv();
|
||||
await runner.start();
|
||||
```
|
||||
|
||||
Exported modules:
|
||||
|
||||
| Export | Purpose |
|
||||
| --- | --- |
|
||||
| `BaseRunner` | Runtime coordinator and heartbeat loop. |
|
||||
| `SupervisorClient` | Minimal Balena Supervisor API client. |
|
||||
| `CloudlyConnector` | Cloudly register/heartbeat HTTP client. |
|
||||
| BaseOS types | Runtime status, desired state, and connector result interfaces. |
|
||||
| `runCli` | CLI entry point for embedding or tests. |
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
deno task start
|
||||
deno task dev
|
||||
deno task check
|
||||
deno task lint
|
||||
deno task fmt
|
||||
deno task test
|
||||
```
|
||||
|
||||
`deno task test` currently delegates to `deno check mod.ts`.
|
||||
|
||||
Source map:
|
||||
|
||||
| Path | Purpose |
|
||||
| --- | --- |
|
||||
| `mod.ts` | Module exports and CLI execution. |
|
||||
| `ts/cli.ts` | Command routing. |
|
||||
| `ts/classes.baserunner.ts` | Main runtime state and heartbeat loop. |
|
||||
| `ts/classes.cloudlyconnector.ts` | Cloudly HTTP client. |
|
||||
| `ts/classes.supervisorclient.ts` | Supervisor API client. |
|
||||
| `ts/types.ts` | Runtime and connector contracts. |
|
||||
| `docker-compose.yml` | balenaOS-style service definition. |
|
||||
| `Dockerfile` | Deno container image for BaseRunner. |
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Treat join tokens, node tokens, and Supervisor API keys as secrets.
|
||||
- Keep the BaseRunner container private to the device; Supervisor API access is powerful.
|
||||
- Prefer short-lived or one-time join tokens in Cloudly.
|
||||
- Do not log token values in device provisioning or fleet diagnostics.
|
||||
- Host OS release signing and host update enforcement are future work, not current functionality.
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
### Trademarks
|
||||
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
||||
|
||||
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
||||
|
||||
### Company Information
|
||||
|
||||
Task Venture Capital GmbH\
|
||||
Registered at District Court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
||||
Reference in New Issue
Block a user