-
Onebox v33.0.0
StableRelease / build-and-release (push) Failing after 11m14sreleased this
2026-09-29 23:59:49 +00:00 | 72 commits to main since this release2026-09-29 - 33.0.0
Breaking Changes
- The package's Docker manager is the Docker runtime backend now, with no aliases: the export
OneboxDockerManagerisDockerRuntimeBackend(with theIOneboxRuntimeBackendtype beside it),Onebox.dockerisOnebox.runtimefor every workload effect andOnebox.dockerRuntimeandOnebox.corestoreEngineDockerfor the Docker-only Corestore conversion steps.DockerImageLoadErroris gone: images no longer travel through the runtime (see below). - A backup that includes the service's image reads it from the registry the service pulled it from, no longer from the runtime (
docker save). The registry must be reachable with the credentials Onebox holds for it and must still serve the image the service runs; an image that was only ever built on the host is refused by name asbackup-image-unavailableand must be pushed to a registry first. A restored image runs from Onebox's local registry (onebox-restore/<service>@sha256:…, with the local registry as the service's registry) instead of by its bare image ID.
Features
- Route every workload effect through one runtime seam, the first step of running Onebox on Pallet without Docker.
IOneboxRuntimeBackend(ts/classes/runtime-backend.ts) states what Onebox needs from a runtime: service and platform workloads, their images, secrets, volumes, published ports, logs, stats and exec, the image and volume artifacts of a backup, the CoreTraffic ingress and managed dcrouter workloads, and the host prerequisitesonebox systemd enableinstalls. Services, platform providers, WorkloadInit approvals, backups and the ops handlers now call onlyonebox.runtime.DockerRuntimeBackend(the formerOneboxDockerManager,ts/classes/docker.ts) implements it on the Docker Engine API and Swarm, with the Docker parts of CoreTraffic (docker-coretraffic.ts), the managed dcrouter (docker-dcrouter.ts), Docker installation andswarm init(docker-host.ts) and the backup'sdocker save,docker loadanddocker cpsteps (DockerArtifactToolsinutils/docker-cli.ts) behind it. The Corestore engine conversion, its target handoffs, its data relocation and its maintenance containers are Docker history and move toCorestoreEngineDockerRuntimeints_migration/corestore-engine-docker.ts; they and the other Corestore migrations reach Docker throughonebox.corestoreEngineDockerandonebox.dockerRuntime. The seam states no Docker shapes: a service workload is created stopped with{ start: false }, an interactive session reports{ running, exitCode }, and a service runtime's secret files carry numeric owner ids, which the Docker backend reads from Swarm's decimal strings and refuses by name when one is not a canonical decimal. Otherwise nothing changes in behaviour: the same Docker requests, CLI commands, unit file and messages. - Administer MariaDB and ClickHouse over their own protocols, from the host, instead of running their command-line clients inside the platform containers: the first slice of taking exec off the paths that need no dump. Provisioning, deprovisioning and the MariaDB health check connect as the admin user through
@lossless.org/client/sqldb(ts/classes/platform-services/sql-admin.ts) to the port the workload publishes on the host's loopback (getContainerHostPort), and the ClickHouse health check asks its HTTP/pingthere. MariaDB binds no parameter in an account statement, so a user is created withIDENTIFIED BY PASSWORDand themysql_native_passwordhash of its password, which is what MariaDB stores forIDENTIFIED BY '<password>'; ClickHouse takes the new password as a query parameter. No password reaches a command line, a process environment or a statement text, and a failure names only the client's error code and the engine's code, never the engine's message. Database dumps and restores still run inside the containers. - Back up and restore a service's image through registries instead of the runtime, so a backup no longer needs the runtime to export or import images (
ts/classes/backup-image.ts). The backup fetches the image over the registry protocol (oci-registry-client.ts) by the service's canonical digest reference or else its configured reference, keeps the first whose index, manifest or config digest is the image ID the runtime reports, and stores it as an OCI image layout with the host platform's manifest, config and layers, each blob verified against its digest as it streams (oci-image-archive.ts);docker loadreads that layout too. A restore reads an OCI image layout, or thedocker-archiveof adocker savefrom Docker before 25, which it converts into an OCI image over the same config and layer bytes after checking each layer against the diff ID its config records; it requires the archive to name the recorded image ID, pushes the image into the local registry underonebox-restore/<service>with the tagrestored-<12 hex digits>(local-registry-image.ts) and has the runtime pull it by digest. Backups and restores of a service without volumes no longer need the Docker CLI. The runtime's artifact tools carry only volumes now (exportImage,importImageand theirdocker save/docker loadsteps are removed). - The dashboard runs in the
dees-appuishell with path URLs: Overview; Workloads (Services, App Store); Domains & Traffic (Domains, DNS, Proxy); Registry (Registry, Tokens); Access; Settings pinned at the bottom. The server's own paths (/typedrequest,/oidc,/.well-known,/v2,/backups,/mcp) stay the server's. The update notice lives in the bottom bar and sign-out in the profile menu. Old dashboard URLs such as/apps/servicesopen the not-found screen with a link to the Overview. - Every page has one header (title, one-line description, actions on the right) and one loading, empty and error pattern; duplicate headings are gone. Views load when they are shown and stop polling, live logs and metrics when they are left.
- Services and platform services open as routed detail pages (
/services/<name>,/platform-services/<type>) on@serve.zone/catalog34's service detail page; every status reads from the platform's shared status vocabulary. - The overview shows compact figures and a "Needs attention" list: Docker down, proxy failed, CoreTraffic takeover failure and last refused route set, failed services and platform services, expired and expiring certificates, and missing gateway permissions.
- Settings is split into Admin UI, Gateway, Storage, Platform and Updates (
/settings/<section>); Access lists users and hosted-app sign-in in tables with "Invite user" in the page header. - Bundle the Cloudly App Store template 1.11.0 for Cloudly 33.8.0 through
@serve.zone/appstore32.11.0, moved from 32.10.0 inpackage.json,pnpm-lock.yamland the binary'sdeno.lock, changing no other package's integrity and keeping the lock's registry origin. Onebox reads the App Store from the catalog this package bundles, soonebox appstore upgrade cloudly --version 1.11.0needs this release. The template pins the Cloudly 33.8.0 image indexcode.foss.global/serve.zone/cloudly@sha256:06ab9a594aecf496fcc16f19bb19fd19123601e11f9615c0d5dcc9be9e540edaand keeps the 1.10.0 runtime contract andminOneboxVersion32.4.0; against 1.10.0 only its image and changelog differ. Cloudly 33.8.0 adds no data step and keeps the data version 0.23.0, so from 1.9.0 or 1.10.0 nothing migrates; the template still marks the upgrade migration-required, backup-before-upgrade, breaking and manual-review, because hosts read these flags from the target version alone and every upgrade from 1.8.0 or earlier runs the forward-only data stepdeployment-machine-user-grants. Deploy Cloudly 33.8.0 before any Pallet that captures workload output reaches a fleet node, since it answersreportRuntimeAssignmentLogswithnot-kept; from 1.4.0 and earlier roll out Cloudly 33.8.0 before Coreflow 33.2.0. The App Store upgrade test now checks that 1.11.0 is the latest catalog version and prepares its upgrade from installs of 1.2.1 through 1.10.0, the latter on the Cloudly 33.7.0 image. App Store 32.11.0 still pins@serve.zone/interfaces32.3.0, so no other runtime package moves.
Fixes
- Security: close the local registry's storage server to everyone but the registry. The S3-compatible storage server that holds the registry's blobs, manifests and tags listened on
0.0.0.0:4000and checked no credentials, so anyone who reached that port could read, overwrite and delete registry data, image layers included, without a/v2/tokengrant. It now listens on127.0.0.1:4000only and refuses every request without a SigV4 signature from a credentialRegistryManagergenerates at each start and passes to its in-process smartregistry, the server's only client; the credential exists only in memory, never on disk or in a log (ts/classes/registry.ts). Registry data stays where it is, so an existing install needs no migration: the next start binds the new address and credential. A test proves the port refuses connections on a non-loopback interface, anonymous reads, writes and deletes, and the old fixedoneboxcredential, while the registry still stores and serves images (test/registry-storage-access.node.ts). - Stop the local registry's storage server again when the registry fails to initialize after starting it.
RegistryManager.init()left the storage server running and the registry uninitialized when smartregistry's own initialization failed, for example when the storage server could not give it its bucket, so the storage port stayed bound and every laterinit()failed withAddress already in use. A failedinit()now closes the smartregistry instance and stops the storage server it started before it rethrows, so the same registry can initialize again (ts/classes/registry.ts). A test fails the initialization with a regular file in the place of the registry's bucket directory, then checks that the port is free and that the same registry initializes on it once the file is gone (test/registry-init.node.ts). - Security:
@push.rocks/smartregistrymoves from 2.10.1 to 2.11.2, which holds the local registry's OCI tokens to what Onebox issues: it compares a token's HS256 signature in constant time, refuses any other algorithm, a missing or non-numericexpand a non-numericnbf, requiresissto be the configured realm andaudto name the configured service, and refuses every OCI token while the OCI token service is disabled. Onebox mints its tokens through smartregistry'screateOciToken()with OCI tokens enabled, so they carry exactly these claims and keep validating. The registry now shuts down throughSmartRegistry.close(), which 2.11.0 added and which also releases the object storage client thatdestroy()left open (ts/classes/registry.ts). 2.11 stores through@lossless.org/clientinstead of@push.rocks/smartbucketand no longer needs@push.rocks/qenvoradm-zip; it brings no package Onebox did not already resolve. The move is inpackage.json,pnpm-lock.yamland the binary'sdeno.lock, changing no other package's integrity and keeping the lock's registry origin. - Make Onebox's local registry usable by Docker and other OCI clients. Its Bearer challenge named a token endpoint (
/v2/token) nothing served, and the registry tokens administrators create were never checked, so no client could pull from or push to it./v2/tokennow issues a signed token, valid 300 seconds, for the requested repository actions the presented HTTP Basic credentials allow (ts/classes/registry-token-service.ts): a stored registry token (compared by SHA-256 in constant time, refused once expired) may push and pull the repositories its scope names, and an internal credential Onebox issues for each of its own runtime's pulls from the local registry may pull every repository and expires after ten minutes; the runtime's registry-auth hook hands that credential for the local registry. Two more defects kept clients out: the registry's OCI base path was empty, so every repository name began withv2/and matched neither tokens nor tag storage (now/v2), and every response body smartregistry streams, blobs among them, was answered as the text{}(a stream now passes through). Tests drive the endpoint over HTTP against smartregistry and its storage server, and the local Docker daemon pulls from it by digest with an internal credential. - Authenticate every pull from a private registry.
@apiclient.xyz/dockerup to 10.1.1 sent theX-Registry-Authheader as unpadded base64url, which the Docker daemon cannot decode and then pulls anonymously without an error: the credentials Onebox holds for a registry reached the daemon only when their JSON length was a multiple of three, about one pull in three, so a private image otherwise failed with an access error. 10.1.2 pads the header.@apiclient.xyz/dockermoves from 10.1.1 to 10.1.2 inpackage.json,pnpm-lock.yamland the binary'sdeno.lock, changing no other package's integrity and keeping the lock's registry origin; regeneratingdeno.lockfor it resolves@push.rocks/smartarchive5.5.1 (withfile-type22.1.1), which@apiclient.xyz/dockerrequires as^5.4.1, wherepnpm-lock.yamlkeeps 5.4.1. No Onebox code changes with it. - A sign-in page that cannot reach Onebox says so ("Onebox is not reachable") with Retry and the technical detail folded away, instead of asking for a trusted hostname and showing the raw exception; every sign-in and hosted-app sign-in error is a sentence.
- Deletes and the restart-causing platform sign-in toggle ask through the dashboard's own confirmation dialog instead of the browser's.
- Volumes entered when creating a service become managed volumes at their container path; the form dropped every volume before.
Maintenance
@serve.zone/interfaces32.15.0 → 32.36.0 and@api.global/typedsocket8.4.0 → 8.7.0 (exact pins) inpackage.json,pnpm-lock.yamland the binary'sdeno.lock, changing no other package's integrity and keeping the lock's registry origin. They are what Onebox needs to control Pallet locally: interfaces 32.36.0 carries the local controller contract (nodeRuntimeLocalControllerContract), local storage claims and the runtime log, stats, exec and volume archive contracts, and typedsocket 8.7.0 reaches a server over a Unix socket (unixSocketPath), which 8.4.0 cannot. No Onebox code changes with them.- The backup import specs are split over three files by concern (
backup-import,backup-import-refusals,backup-import-space) with their two host doubles intest/helpers/backup-import.ts. Each double initializes its own encrypted archive, and Argon2id derives the passphrase key at every initialization (about 2–3 s on a loaded host), so the single file needed 56–76 s against its 60-second limit; each file now takes about 20 s. mariadb3.5.4 (LGPL-2.1-or-later) and@clickhouse/client1.23.1 (Apache-2.0) are runtime dependencies now, the drivers@lossless.org/client/sqldbloads for MariaDB and ClickHouse, inpackage.json,pnpm-lock.yamland the binary'sdeno.lock. The release binaries'THIRD_PARTY_NOTICES.mdreproduces their license files, and those ofdenqueandlru-cache, whichmariadbbrings, verbatim from Deno's npm cache at the versions the binary'sdeno.lockresolves (scripts/binary-notices.ts);mariadb's section states that it is LGPL-2.1-or-later, where its corresponding source is (its npm tarball, pinned by the release's lockfiles, and the upstream tag) and how to build Onebox with a modified copy. The release workflow checks the published notices for each of these texts (pnpm run check:binary-notices).- Release tooling:
@git.zone/tsdeno^1.12.0→^1.14.0(with@git.zone/tsbundle3.1.0 through it). The text of@epic-web/invariant, which ships no license file and reaches the binary throughargon2, is named as athirdPartyNoticessupplement in.smartconfig.json, pointing atnotices/third-party-notices.supplement.md. tsdeno's notices stay disabled until the libraries that embed license-less packages supply their texts; the release keeps assemblingTHIRD_PARTY_NOTICES.mdwithscripts/binary-notices.ts. - The WorkloadInit approval specs are split over four files by concern (
workloadinit-approvalsfor the lifecycle,-restore,-deployand-delete) with their shared fixture intest/helpers/workloadinit-approvals.ts. Every one of the 38 cases starts its own database and commits the approval steps as SmartDB transactions, each of which takes about 60–120 ms on this host (its journal is staged, renamed and fsynced file by file), so the single file needed about 50–58 s against its 60-second limit; each file now takes 13–20 s. @design.estate/dees-catalog^16.17.0 and@serve.zone/catalog^34.1.0, one copy of each in the lockfile: status badges carry the status dot, and the catalog list views open a row with a single click. Removed dashboard code no route reached (the proxy view's DNS and domain branches, the services quick-deploy and backups sub-views, and an external-registries stub that only logged).- Release tooling currency:
packageManagermoves topnpm@12.6.0and@git.zone/tsbundleto^3.1.0, the version the lockfile already resolved;@git.zone/tsbuild^5.0.0,@git.zone/tsdeno^1.14.0,@git.zone/tsrun^3.0.2,@git.zone/tstest6.3.2 and@git.zone/tswatch^4.0.2are already the current releases. pnpm and@types/nodestop at the newest releases at least seven days old: pnpm 12.7.0 and@types/node26.6.3 are younger, so@types/nodestays at 26.6.2. No migration is due: the project has no foldertsconfig.jsonortspublish.json(tsbuild 5), the dashboard bundle already writes the third-party notices tsbundle 3 requires, and no script readsprocess.argvthrough tsrun (tsrun 3). The release workflow takes pnpm frompackageManagerthrough corepack and asserts no pnpm version of its own; the lockfile keeps the npmjs sha512 integrity of every package.
Downloads
- The package's Docker manager is the Docker runtime backend now, with no aliases: the export