# @serve.zone/onebox

Onebox is a self-hosted application platform for a single server. It combines Docker, CoreTraffic routing, a typed web control plane, app templates, platform services, and containerarchive-powered backups into one NodeNext TypeScript package and service.

## Issue Reporting and Security

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.

## What Onebox Does

Onebox turns a Linux host into a small PaaS that can run your own containers and curated app templates without a separate control plane. It is designed for the "one good server" use case: one machine, one local Docker runtime, one web dashboard, one operational surface.

- Deploys Docker workloads from external images or Onebox App Store templates.
- Uses the local Docker socket and creates the `onebox-network` network automatically.
- Runs workloads as Docker Swarm services when Swarm is active, otherwise as standalone containers.
- Starts a CoreTraffic-backed reverse proxy for HTTP/S routing and WebSocket traffic.
- Serves the web UI and TypedRequest/TypedSocket API through `OpsServer` on port `3000` by default.
- Reports every App Store-managed service's platform OIDC eligibility through `getUsersAndAppRoles.response.platformOidcQualifications`, including a stable qualification code/reason, declared roles, and an optional registration view.
- Shows every App Store-managed service in Access, including the same stable qualification code/reason when platform OIDC is unavailable.
- Exposes an admin-authenticated read-only MCP endpoint at `/mcp` for safe service, domain, and platform-service summaries.
- Stores platform state in a local SmartDB database.
- Provisions database and object-storage dependencies through one Corestore platform service. Database clients intentionally use the MongoDB wire protocol and standard `MONGODB_*` connection variables.
- Tracks domains, Cloudflare DNS records, ACME certificates, service logs, metrics, backup schedules, and app template metadata.
- Can sync routes and import certificates from an external `dcrouter` gateway when configured.

## Architecture

```text
browser / CLI
    |
    v
OpsServer :3000
  - bundled web UI
  - TypedRequest handlers
  - TypedSocket dashboard events
    |
    v
Onebox coordinator
  - SmartDB repositories
  - Docker manager
  - CoreTraffic route manager
  - DNS and SSL managers
  - platform service providers
  - app store manager
  - backup manager and scheduler
    |
    v
Docker host
  - onebox-network
  - CoreTraffic
  - user services
  - optional platform services
```

`Onebox` is the central class. It initializes the database, Docker, CoreTraffic, DNS, SSL, platform services, App Store, backup subsystem, optional external gateway integration, and the web/API server.

## Installation

Install the latest released Linux binary:

```bash
curl -sSL 'https://code.foss.global/api/v1/repos/serve.zone/onebox/raw/install.sh?ref=main' | sudo bash
```

The installer downloads `onebox-linux-x64` or `onebox-linux-arm64` from the latest Gitea release, installs it under `/opt/onebox`, and links `/usr/local/bin/onebox`. Use `--version vX.Y.Z` to pin a release, `--install-dir /path` to change the target directory, or `--source` to clone the tag and build the NodeNext package locally.

```bash
curl -sSL 'https://code.foss.global/api/v1/repos/serve.zone/onebox/raw/install.sh?ref=main' | sudo bash -s -- --source
```

For source checkouts, install dependencies and build directly:

```bash
pnpm install
pnpm run build
node ./cli.js --help
```

This repository currently marks the npm package as private; use the installer or a source checkout until public npm release packaging is enabled.

## Quick Start

Run a foreground development instance:

```bash
onebox server --ephemeral
```

Open the dashboard:

```text
http://localhost:3000
```

On the first browser visit, Onebox asks you to create the administrator passkey. Enter the short-lived bootstrap code printed in the local Onebox service logs, then complete the browser passkey prompt. The code authorizes first-time enrollment only; Onebox never accepts a human password.

Production passkeys require a stable HTTPS hostname. While the Onebox coordinator is running, persist it from another terminal with `onebox config set adminUiDomain onebox.example.com`, then restart Onebox before opening the production UI and enrolling the passkey.

`ONEBOX_ADMIN_UI_DOMAIN=onebox.example.com` is an alternative only when added to the Onebox systemd service environment; exporting it in an interactive shell does not update the generated unit. After enrollment, the hostname is pinned to the passkey relying party.

If every passkey for the configured administrator is lost, use local host access to reset enrollment:

```bash
sudo onebox systemd stop
sudo onebox auth reset-passkeys --confirm
sudo onebox systemd start
sudo onebox systemd logs
```

The reset invalidates every passkey and session for the configured administrator. It never enables password authentication. The production database defaults to `/var/lib/onebox/.nogit/smartdb`; use `--database-path` only for a nonstandard installation.

Deploy a simple service:

```bash
onebox service add web --image nginx:latest --domain web.example.com --port 80
```

For production, install and run the systemd service:

```bash
sudo onebox systemd enable
sudo onebox systemd start
sudo onebox systemd logs
```

The systemd unit runs `onebox systemd start-daemon` with `/var/lib/onebox` as its working directory. From source or foreground runs, the default SmartDB path is `./.nogit/smartdb` relative to the current working directory.

## CLI Reference

```bash
onebox <command> [options]
```

Core commands:

| Command | Purpose |
| --- | --- |
| `selftest typed-rpc [--port <port>]` | Start and stop an isolated server to verify the packaged Typed RPC runtime graph. |
| `server [--ephemeral] [--port <port>] [--monitor]` | Start the web/API server in the foreground. |
| `service add <name> --image <image> [--domain <domain>] [--port <port>] [--env KEY=VALUE]` | Deploy a workload. |
| `service list` | List known services. |
| `service start <name>` | Start a stopped service. |
| `service stop <name>` | Stop a running service. |
| `service restart <name>` | Restart a service. |
| `service remove <name>` | Remove a service and its route. |
| `service logs <name>` | Print Docker logs for a service. |
| `appstore list` | List remote app templates. |
| `appstore config <app-id> [--version <version>]` | Print app metadata and version config. |
| `appstore install <app-id> --name <name> [--domain <domain>] [--version <version>] [--env KEY=VALUE] [--secrets-stdin]` | Install an app template. Pass declared secret overrides as one JSON object through stdin; `--env` accepts only non-secret values. |
| `appstore upgrade <service-name> [--version <version>]` | Upgrade an App Store-managed service through the forward-only catalog gate. |
| `registry add --url <url> --username <user> --password-stdin` | Store encrypted external registry credentials without exposing the password in process arguments. |
| `registry remove --url <url>` | Remove registry credentials. |
| `registry list` | List configured registries. |
| `dns add <domain>` | Add a DNS record through the configured DNS manager. |
| `dns sync` | Sync Cloudflare domains into Onebox. |
| `ssl renew [domain]` | Renew one certificate or expiring certificates. |
| `ssl list` | List stored certificates. |
| `ssl force-renew <domain>` | Force certificate renewal for a domain. |
| `proxy reload` | Reload routes and certificates into CoreTraffic. |
| `proxy test` | Check reverse proxy state. |
| `proxy status` | Print route/certificate counts and ports. |
| `systemd enable` | Install and enable the systemd unit. |
| `systemd disable` | Stop, disable, and remove the systemd unit. |
| `systemd start` | Start Onebox through systemd. |
| `systemd stop` | Stop Onebox through systemd. |
| `systemd status` | Show service status. |
| `systemd logs` | Follow `journalctl` logs. |
| `config show` | Show stored settings with secret values masked. |
| `config set <key> <value>` | Store a non-secret setting. |
| `config set <secret-key> --stdin` | Store one bounded, single-line secret without placing it in process arguments. |
| `backup create <service-name>` | Create a containerarchive backup through the running root-local coordinator. |
| `backup list [service-name]` | List completed backups, optionally for one service. |
| `cloudly-secrets-v2 <phase> <service-name>` | Inspect or operate the manually gated Cloudly Secrets v2 cutover, including WorkloadInit approval staging through bounded stdin. |
| `auth reset-passkeys --confirm [--database-path <path>]` | Locally invalidate every passkey for the configured administrator and prepare fresh enrollment. |
| `migration corestore-platform --status` | Read redacted cutover status without initializing or mutating SmartDB. |
| `migration corestore-platform --approve --checkpoint-reference <reference> [--allow-empty-legacy-mongodb]` | Offline, resumable cutover from split legacy storage/database services to one Corestore owner. |
| `status` | Print JSON system status. |
| `upgrade` | Install the latest released package build. Requires root. |

App Store upgrades fail closed unless the fresh index, app metadata, and target config agree. Semantic-version targets must be newer than the installed version. Branch and digest-tracked upgrades must use the fresh latest target. Onebox rejects stale operations when the service template or installed version changes before migration or apply.

The legacy `nginx` command name is still accepted as an alias for `proxy`, but CoreTraffic is the active proxy backend.

### Corestore platform cutover

Fresh database and object-storage resources are owned by one pinned Corestore service. SmartDB replaces the MongoDB backend while deliberately retaining the MongoDB wire protocol, port `27017`, driver compatibility, and `MONGODB_*` workload variables. Onebox does not run a standalone MongoDB platform service for new deployments.

Legacy split ownership is converted only by the explicit migration in `ts_migration/`. Before running it, stop Onebox and create a separately stored, verified, restorable full checkpoint of the authoritative Corestore volume. Pass the operator checkpoint identifier unchanged on every retry:

```bash
sudo onebox systemd stop
sudo onebox migration corestore-platform --status
sudo onebox migration corestore-platform --approve \
  --checkpoint-reference <verified-checkpoint-reference>
sudo onebox migration corestore-platform --status
sudo onebox systemd start
```

The migration captures exact platform/resource ownership, fences affected workloads, renames the legacy object-storage owner to Corestore, provisions target resources, converts non-empty legacy databases with the pinned Corestore migration image, atomically replaces resource credentials, and restarts only workloads that were running before the cutover. Each phase is persisted and replay-safe. Database conversion temporarily leases the canonical `onebox-mongodb` name to the exact captured source container so its persisted replica-set identity remains valid. A stopped canonical-name holder is displaced and retained only when its exact command, networks, data volume, key-file bind, and mount topology prove it is a legacy twin of the captured source; running or unrelated owners fail closed without MongoDB mutation. The migration restores the exact captured recovery name before advancing the phase. Replaying an already-complete migration re-provisions and verifies the canonical credential tuple and reports `repaired` when persisted credentials or mapped service settings changed. A service held by a pending Cloudly Secrets v2 backup remains stopped; its environment and encrypted secret bundle are reconciled through the exact persisted backup fence without creating or starting a runtime. Other running services are restarted only when credential reconciliation changes their delivered settings. Recovery containers are renamed and retained; the command prints their names so an operator can remove them only after production verification and the agreed recovery window.

An empty legacy database is recorded as a persisted inspection proof and fails closed on the first run. After reviewing that proof, rerun the same command with `--allow-empty-legacy-mongodb`. This approval skips only databases re-inspected as empty; it does not disable verification for non-empty databases. Normal Onebox startup never performs this infrastructure cutover implicitly.

`migration corestore-platform --status` uses SmartDB's bounded Linux read-only management sidecar while Onebox is stopped. It reads only the exact migration-state string and does not start a database listener, initialize SmartData, run storage migrations, repair storage, or write files. The packaged SmartDB Rust sidecar must be available. Output is limited to the phase, SHA-256 hashes of the checkpoint reference and captured authority, resource/progress/recovery counts, and creation/update timestamps; checkpoint references, service and resource names, owner IDs, database names, and container identities are never emitted.

### Cloudly Secrets v2 cutover

The Cloudly Secrets v2 transition is a manual, forward-only App Store cutover. The exact schema-4 target is App Store version `1.1.5` from `@serve.zone/appstore` `2.4.0`, requires Onebox `7.5.0` or newer, and uses the released multiarch Cloudly `19.2.0` digest `sha256:7aec3b1f553292cefd5fd421a70e6c50463af168b14b1e4b127597991efa30dd`. An unreleased checkout, another App Store version, or a mutable image tag is not a valid target. The App Store version declares the target image and application configuration; Onebox owns the secret-file identities for the keyring, authorization, rehearsal receipt, and WorkloadInit approval artifact.

Run each gate through the active root-local Onebox coordinator and inspect status between phases:

```bash
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 begin cloudly --target-version 1.1.5
sudo onebox cloudly-secrets-v2 hold cloudly
sudo onebox cloudly-secrets-v2 backup cloudly
sudo onebox cloudly-secrets-v2 verify-backup cloudly
sudo onebox cloudly-secrets-v2 stage-workloadinit-approval cloudly --stdin \
  < cloudly-workloadinit-approval.json
sudo onebox cloudly-secrets-v2 rehearse cloudly
sudo onebox cloudly-secrets-v2 prepare-keyring cloudly
sudo onebox cloudly-secrets-v2 apply-target cloudly
sudo onebox cloudly-secrets-v2 authorize cloudly
sudo onebox cloudly-secrets-v2 execute cloudly
sudo onebox cloudly-secrets-v2 verify cloudly
sudo onebox cloudly-secrets-v2 verify-restart cloudly
sudo onebox cloudly-secrets-v2 promote cloudly
```

An eligible schema-1 or schema-2 operation against App Store `1.1.2` or `1.1.3` must first resume through the existing schema-3 advance and rehearsal path:

```bash
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 advance-target cloudly
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 rehearse cloudly
sudo onebox cloudly-secrets-v2 status cloudly
```

That advance pins App Store `2.3.0` and Cloudly `17.1.0`, preserves its existing encrypted keyring when present, and appends immutable target, compatibility-fence, backup, keyring, runtime-hold, package, and target-advance evidence. It does not start Cloudly, regenerate an existing keyring, or alter the verified backup.

An eligible schema-3 operation against App Store `1.1.4` may then move to schema 4 only from its exact `backup-verified` state with complete backup evidence, a persisted `run-rehearsal` intent and scratch lineage, and a proven zero-task runtime. Run the explicit supersession, stage the verified authority, and start a fresh rehearsal before preparing the production keyring:

```bash
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 advance-target cloudly
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 stage-workloadinit-approval cloudly --stdin \
  < cloudly-workloadinit-approval.json
sudo onebox cloudly-secrets-v2 rehearse cloudly
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 prepare-keyring cloudly
```

The supersession pins the published App Store `2.4.0` index, manifest, config, OCI index, and platform identities in one revision-fenced schema-4 transition. It retains the complete canonical schema-3 predecessor in an immutable audit, preserves the verified backup and optional encrypted keyring digest, and clears the current run, scratch, and pending intent so predecessor scratch ownership remains historical and inert. It does not start Cloudly, regenerate an existing keyring, clean predecessor resources, or alter the verified backup. Any other predecessor shape fails closed.

`stage-workloadinit-approval` accepts one active or revoked v1 artifact only through the root-local control socket and bounded multiline stdin. `@serve.zone/workloadinit` is the sole parser and verifier. Onebox persists a durable intent, performs a generation-fenced encrypted reserved-secret CAS with digest readback, then checkpoints immutable authority history. The target receives the artifact only at `/run/secrets/cloudly-workloadinit-approval.json`, owned by root with mode `0400`; it is excluded from environment and maintenance-container metadata. The initial authority must be active and is staged while schema 4 remains held at `backup-verified`. Rehearsal remains blocked until that checkpoint exists.

After promotion, an authority update or matching revocation must use a strictly higher generation. Onebox recreates the promoted runtime with the new Docker secret, restores its prior running or stopped state, verifies the exact read-only mapping, and only then advances the authority checkpoint. If replacement is interrupted, startup scales the pending runtime to zero; rerunning the exact same staging command resumes from the persisted intent and secret digest. Generic service-secret APIs cannot add, change, or remove the reserved approval key.

If `backup` remains pending because Corestore credentials require canonical migration repair, first require `status` to show phase `held`, pending intent `create-backup`, and zero desired, active, and running tasks. Then stop the sole Onebox coordinator and replay the original Corestore migration from the systemd working directory with the same verified checkpoint reference used for the first migration:

```bash
cd /var/lib/onebox
sudo onebox systemd stop
sudo onebox migration corestore-platform --status
sudo onebox migration corestore-platform --approve \
  --checkpoint-reference <original-verified-checkpoint-reference>
sudo onebox systemd start
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 backup cloudly
```

Do not continue if the offline replay fails, if Onebox does not restart cleanly, or if the post-start cutover status and zero-runtime proof changed. The repair does not start the held Cloudly workload and the final `backup` command resumes the existing `create-backup` intent rather than creating a second cutover operation.

Before `verify-backup`, an existing installation must likewise replay the original Corestore migration so the persisted Corestore owner adopts the current Onebox-pinned image that provides canonical database digests and allocation-fenced scratch databases. Stop the sole Onebox coordinator, require `migration corestore-platform --status` to show the expected persisted migration phase and hashes, and rerun `migration corestore-platform --approve` with the original verified checkpoint reference. The replay accepts only known prior Corestore image digests, proves the old container stopped, persists the new immutable image before deployment, and leaves a held Cloudly workload stopped. After restarting Onebox, require the same cutover operation ID, phase, pending intent, backup evidence, and zero-runtime proof before rerunning `verify-backup`.

`authorize` records the explicit operator approval. `execute` separately mints the short-lived, single-run production authorization while the service is proven quiescent. Rehearsal and production authorizations are mounted only into their operation-owned maintenance containers and are removed after use; they are never delivered to the hosted Cloudly runtime. The encrypted rehearsal receipt exists only until it binds production preparation and is then removed. Onebox exposes plaintext maintenance material on the host only through operation-owned mode-`0600` staging. The hosted target receives the encrypted-bundle keyring and verified WorkloadInit approval at their dedicated root-owned mode-`0400` paths.

`begin` derives the historical source from the owned immutable Docker runtime and never pulls a mutable source tag. If a prior Onebox defect replaced a stopped runtime's digest reference with a tag, recovery requires `--source-image code.foss.global/serve.zone/cloudly@sha256:<digest>`. Onebox accepts that input only while the persisted service is stopped and the Swarm runtime has zero desired replicas and no active tasks. It verifies the exact local image, proves that every filtered task belongs to the exact service and is terminal, and pins the existing stopped service through Docker's version fence before persisting the cutover. The full recovery transition is serialized against Onebox service lifecycle mutations. If the persisted-service CAS fails after the runtime pin, the runtime remains stopped at the verified digest, but the operator must inspect the current service record before retrying `begin`; no cutover is claimed until that original snapshot is persisted successfully.

Every non-promoted operation is held at zero during Onebox startup before routes, gateway export, platform-secret reconciliation, App Store upgrades, or background work can reactivate it. A command interrupted with a persisted pending intent can be rerun for that phase. Maintenance attempts use deterministic identities so a completed result can be adopted after a lost response; Onebox persists terminal ownership before parsing output, records explicit completed zero-blocker proof, and removes the candidate only after proving its exact receipt and absence. Failed verification restores and proves the zero-runtime hold. Startup after `restart-verified` returns the operation to `verified`, requiring restart verification to run again before promotion.

A pending `prepare-keyring` replay reuses and validates the already encrypted Onebox-owned keyring instead of generating or replacing it. A fresh preparation fails closed if the reserved key already exists. Legacy non-inspect `CUTOVER_BLOCKED` failures display only validated blocker codes and include the total count when the bounded blocker list is truncated; references and malformed evidence are not echoed.

`diagnose` is available only after a rehearsal stops at the prepared scratch inspection checkpoint. It cannot persist a cutover transition or advance the rehearsal:

```bash
sudo onebox cloudly-secrets-v2 status cloudly
sudo onebox cloudly-secrets-v2 diagnose cloudly
sudo onebox cloudly-secrets-v2 status cloudly
```

Inspection runs from the released Cloudly `19.0.0` diagnostic image at exact multiarch digest `sha256:f295d471628934e234ba9acd4db362c27e3d863b96f7cdcdcc7f04b11d7374c1`; schema-3 maintenance remains pinned to Cloudly `17.1.0`, while schema-4 maintenance and the hosted target use the exact Cloudly `19.2.0` release described above. Diagnosis also requires the persisted and running Corestore service to prove the exact released `5.0.0` image at digest `sha256:e3e25e435b58d45408e1659a90a3f8de98390d6bbe9ed8b3f40d80d6aa92a567`; Onebox does not upgrade that service implicitly. Corestore issues one fixed 12-minute `read` grant bound to the persisted scratch allocation, and Onebox sends its MongoDB URI only over attached stdin to a non-restarting, auto-removing diagnostic container. Onebox journals the grant and container identities before issuance, retains the journal until revocation and container absence both succeed, and reconciles retained journals before normal startup or another diagnosis continues. A coordinator-wide lock serializes issuance, reconciliation, and shutdown cleanup; reconciliation attempts every valid journal even when siblings are malformed or fail, and capacity is checked before any new journal is written. Docker attach and wait transports are cancelled at the lifecycle deadline, lost-create adoption must match the complete inspected execution identity, and Corestore control errors are read through the same 16 KiB bound as private grant responses. Onebox accepts only diagnostics schema `2`, validates each finite code/reason pair and the complete aggregate counts, and rejects references, unknown fields, malformed output, and unapproved reasons without echoing their values. Run `rehearse` again only after the reported blockers have exact approved corrections.

Upgrading an existing Corestore `4.5.0` owner to this diagnostic runtime is an explicit offline replay, not a startup action. First prove every Corestore consumer quiescent, stop the sole Onebox coordinator, stop and prove the exact Corestore container stopped, then create and independently verify a fresh full-volume checkpoint outside the authoritative Docker volume. This fresh pre-`5.0.0` checkpoint is recovery evidence; the replay command must still receive the original checkpoint reference already captured in the persisted migration state:

```bash
cd /var/lib/onebox
sudo onebox migration corestore-platform --status
sudo onebox migration corestore-platform --approve \
  --checkpoint-reference <original-verified-checkpoint-reference>
sudo onebox migration corestore-platform --status
```

The replay accepts the exact known `4.5.0` image, durably selects `5.0.0`, and keeps every non-promoted Cloudly cutover workload stopped. SmartDB `4.x` then migrates persistent auth metadata to version 3 on first Corestore `5.0.0` startup. That transition is one-way: retry Corestore `5.0.0` after an interrupted start, or restore the fresh pre-`5.0.0` checkpoint before selecting Corestore `4.5.0`/SmartDB `3.x`. Never start the older runtime against a volume that Corestore `5.0.0` has opened.

`verify-backup` performs full archive-integrity verification and validates the exact Corestore backup receipt. `rehearse` then provisions an expected-absent pinned Corestore scratch database, persists its allocation identity before restore, validates the restore response, and compares Corestore's bounded canonical source and restored content digests plus every scan counter. The same persisted rehearsal and allocation identity fences provisioning, restore, digest and status checks, and cleanup; completion requires a fenced absence proof. This proof streams the database scan without using the size-limited portable export payload. The scratch proof covers the selected database only; it does not prove restoration of application object-storage resources. The authenticated Onebox full-backup restore workflow remains responsible for restoring and verifying every supported resource represented by the backup. Named object-storage bindings and requests are currently unsupported by that workflow and make backup creation fail closed.

Rollback mode is exclusively `full-restore-only`. Once `begin` has persisted the cutover, there is no cancel command and no old-image rollback command. Recovery must use the authenticated Onebox full-backup restore workflow; replacing the image or metadata manually is not a supported rollback. Promotion is available only after the target starts successfully, reports a verified-completed rollback-fenced status, and passes a second restart without authorization.

## Configuration Notes

Useful settings include:

| Setting | Purpose |
| --- | --- |
| `adminUiDomain` | Exact HTTPS hostname used as the WebAuthn relying-party ID. |
| `serverIP` | IP address used for DNS records. |
| `cloudflareToken` | Cloudflare API token. `cloudflareAPIKey` is accepted as a legacy alias. |
| `cloudflareZoneId` | Cloudflare zone identifier. |
| `acmeEmail` | ACME account email for certificate issuance. |
| `httpPort` | OpsServer/web UI port. Defaults to `3000`. |
| `metricsInterval` | Metrics collection interval in milliseconds. |
| `backupPassword` | Secret passphrase for encrypted backup repositories. |
| `dcrouterGatewayUrl` | Optional external dcrouter API endpoint. |
| `dcrouterGatewayApiToken` | Optional external dcrouter API token. |
| `dcrouterGatewayClientId` | Optional stable external gateway client identity used for route ownership. |
| `dcrouterTargetHost` | Optional target host advertised to dcrouter. |
| `dcrouterTargetPort` | Optional target port advertised to dcrouter. |

Example:

```bash
onebox config set serverIP 203.0.113.10
onebox config set acmeEmail ops@example.com
onebox config set cloudflareToken --stdin
onebox config set cloudflareZoneId zone-id
```

For the stdin form, enter or pipe one secret line and then close stdin.

External registry credentials remain encrypted in Onebox and are decrypted only for the exact Docker image-pull request that needs them. Onebox does not persist them through `docker login`.

## App Store

The App Store manager fetches metadata from `serve.zone/appstore` through `@serve.zone/appstore` and caches it briefly. Templates can declare public, secret, and generated environment inputs, container arguments, file-mounted secrets, and platform requirements. Installing an app can automatically provision Corestore database/object-storage, ClickHouse, Valkey, or MariaDB resources.

Service secrets and provisioned platform credentials are encrypted at rest. The browser seals each declared secret to Onebox's active X25519 recipient with install-specific authenticated context before the TypedRequest call. The root CLI accepts secrets only through bounded stdin over its mode-`0600` local control socket; the daemon seals them before constructing the install request. Generated values are created inside Onebox. Plaintext install secrets never enter the public environment, browser request cache, process arguments, or persisted App Store metadata. Environment secrets work with standalone Docker containers and Swarm services. File-delivered secrets stay out of the environment and are mounted through Docker secrets, so Onebox rejects them when the host is not using Swarm mode.

Hosted apps authenticate to Onebox with signed, audience-scoped RS256 machine JWTs stored in the encrypted service-secret bundle. Onebox verifies both the signature and the exact currently active token, rotates legacy random tokens and near-expiry JWTs, and reconciles changed identities through the normal service update path. Bootstrap actions use typed message/setup-route variants and revision-fenced atomic transitions; concurrent lifecycle changes fail closed.

### Named storage contracts

App Store versions can declare template-local `storageClasses` and stable `storageRequests` for `filesystem` and `objectStorage` resources. These declarations contain portable requirements only. Provider names, host paths, network shares, mount options, and authentication details remain Onebox operator configuration and never enter an app manifest.

Onebox normalizes every install through `@serve.zone/appstore`, negotiates the declared storage feature IDs, selects a compatible operator class deterministically, and persists the canonical resolved binding on the service. Filesystem requests become service-owned Docker volumes. Each object-storage request receives its own bucket, endpoint, and scoped credential material through the Corestore v2 binding API; multiple named bindings use disjoint environment keys and encrypted service-secret storage.

Legacy `volumes` and `platformRequirements.s3: true` declarations are normalized for new installs. Existing pre-binding services remain on their legacy resources during compatible upgrades. Adding or changing an explicit named request requires a storage migration instead of silently replacing data.

The current fulfillment adapter advertises only local/backup filesystem classes and the standard managed object-storage policy with environment delivery. Capacity requests, secret-file object delivery, hard quotas, snapshots, versioning, non-local filesystem targets, and Kerberos-authenticated NFS remain unadvertised and fail closed. Onebox does not fall back to local storage when a requested capability or mapped class is unavailable. Reclaim policy is honored as `retain` or `delete` during rollback and service removal.

Backups export and restore opted-in filesystem volume data by mount path while preserving the portable storage specification, together with supported legacy platform resources. Named object-storage bindings and requests remain unsupported; backup creation fails closed when either is present.

```bash
onebox appstore list
onebox appstore config cloudly
onebox appstore install cloudly --name cloudly --domain cloudly.example.com
```

## Backups

Backups are built around `@serve.zone/containerarchive`. Onebox exports service configuration, encrypted service-secret ciphertext, platform resource metadata, supported platform data, and optionally Docker images into a content-addressed archive repository. Platform data is streamed as a fixed, bounded set of deterministic logical tar items rather than one archive item per file. Restore derives large-item limits from authenticated snapshot metadata and validates tar paths, entry types, sizes, and symlink boundaries before importing data.

S3-compatible resources store object bodies under SHA-256 identifiers and retain exact object keys in a validated versioned manifest, so an object key is never interpreted as a filesystem path. Host-side export of a Corestore-owned bucket requires the canonical internal endpoint `http://onebox-corestore:9000`, the exact `onebox-corestore` container identity, and its loopback-published S3 port. Stale `onebox-smartstorage` credentials, malformed Corestore endpoints, and missing host publications fail closed and require the explicit Corestore migration replay above; Onebox does not add a host alias or persist a loopback endpoint into workload credentials. Compatibility readers remain isolated for earlier untagged file-item snapshots and `.tar.enc` backups; new backups write only the logical-item format. Legacy `.tar.enc` restores migrate plaintext control and platform credentials into the current representation.

Image-inclusive backups always resolve the active Docker image ID and export by that immutable ID. A canonical digest or mutable tag is retained only while it resolves to the active image; an absent or moved mutable tag is discarded in favor of the verified image ID, while a conflicting immutable digest fails closed. Restore verifies the archived image ID before changing service state, never reassigns a mutable tag, and never replaces archived content with a registry pull.

Encrypted service-secret bundles can be decrypted only with the same credential-encryption key. Set the same 32-byte, base64-encoded `ONEBOX_ENCRYPTION_KEY` on another host before restoring there; without an explicit key, Onebox derives one from the hostname and `/etc/machine-id`, so ciphertext is portable only to a host with the same machine identity.

Backup and schedule operations are primarily exposed through the OpsServer/web UI handlers.
Root operators can also run `onebox backup create <service-name>` and
`onebox backup list [service-name]`; both commands execute inside the sole
coordinator through the root-only Unix control socket. Backup creation is
serialized per service across CLI, UI, and scheduled callers.

## Development

Requirements:

- Node.js for the application runtime.
- pnpm for package scripts.
- Docker for any runtime path that initializes Onebox fully.

Common tasks:

```bash
pnpm run watch
pnpm run build
pnpm test
node ./cli.ts.js server --ephemeral --monitor
```

Source map:

| Path | Purpose |
| --- | --- |
| `cli.js` | Built CLI entry point. |
| `cli.ts.js` | Source CLI entry point for development. |
| `ts/cli.ts` | CLI router and command help. |
| `ts/classes/onebox.ts` | Main coordinator. |
| `ts/classes/docker.ts` | Docker client, networks, containers, and Swarm services. |
| `ts/classes/coretraffic.ts` | CoreTraffic Docker service and Admin API manager. |
| `ts/classes/reverseproxy.ts` | CoreTraffic route and certificate bridge. |
| `ts/classes/platform-services/` | Local platform service providers. |
| `ts/classes/appstore.ts` | Remote App Store catalog and upgrade logic. |
| `ts_migration/cloudly-secrets-v2-cutover.ts` | Persisted, phase-gated Cloudly Secrets v2 cutover coordinator. |
| `ts/classes/storage-manager.ts` | Portable storage capability negotiation, class selection, binding fulfillment, and lifecycle. |
| `ts/classes/backup-manager.ts` | Backup and restore orchestration. |
| `ts/opsserver/` | Web UI server and TypedRequest handlers. |
| `ts/database/` | SmartDB repositories. |
| `ts_migration/` | Versioned persisted-data and infrastructure migrations. |
| `ts_web/` | Dashboard source. |

## License and Legal Information

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in [license.md](./license.md).

**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<br>
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.
