Installed CLI Architecture
Technical contract for the portable Oblive operator CLI, deployment bundle, runtime generation, updates, and compatibility tests.
The installed oblive CLI is host operator tooling for a portable Oblive deployment. It owns the
installation lifecycle around the complete Docker Compose stack: configuration generation,
authentication preflight, image selection, service startup, inspection, coordinated updates, and
explicit data removal.
It is not an application control-plane client. oblivectl is the authenticated, agent-only CLI for
durable product transitions, while contextctl is a local read-only context helper. Keep deployment
operations in oblive; do not add application mutations or agent capabilities to it.
Source and Artifact Ownership
| Concern | Canonical source | Released artifact |
|---|---|---|
| Operator command implementation | scripts/release/oblive | oblive |
| Typed config/runtime generator | scripts/portable-stack.ts | lib/portable-stack.js |
| Base topology | compose.yaml | compose.yaml |
| Local Codex auth topology | compose.codex-local.yaml | compose.codex-local.yaml |
| Configuration contract | config/local-stack.schema.json | local-stack.schema.json |
| Bundle assembly | .github/scripts/release/build-deployment-bundle.sh | oblive-deployment.tar.gz |
| Public bootstrap | sites/landing/public/install.sh | https://oblive.dev/install.sh |
The CLI is POSIX sh so the installed deployment does not require a host Bun or a repository
checkout. Bundle construction still requires Bun because it compiles the TypeScript generator into
one dependency-bundled JavaScript file.
The release archive has a deliberately small, exact file set:
IMAGE_REPOSITORY
VERSION
compose.codex-local.yaml
compose.yaml
docker/garage.toml
docker/postgres/001-vector.sql
lib/portable-stack.js
local-stack.schema.json
obliveDo not add source-only files, package-manager state, build directives, or floating image references to the portable artifact.
Installation Layout and Root Resolution
The default bootstrap creates:
~/.local/bin/oblive -> ~/.oblive/oblive
~/.config/oblive/install-root
~/.oblive/XDG_CONFIG_HOME changes the pointer directory, OBLIVE_BIN_DIR changes the bootstrap symlink
directory, and --install-dir changes the installed root. The CLI resolves its root in this order:
OBLIVE_HOME, when set;- the first line of
$XDG_CONFIG_HOME/oblive/install-root; or ~/.oblive.
The root must not be empty, /, the user’s home directory, or contain :. OBLIVE_HOME is useful
for tests and deliberate custom invocations, but the install pointer is the normal runtime contract
for a non-default installation.
Release-owned and machine-owned files remain separate:
| File or directory | Owner | Update behavior |
|---|---|---|
VERSION | Release | Replaced with the coordinated semantic version |
IMAGE_REPOSITORY | Release | Replaced with the release repository |
Compose and lib/ | Release | Replaced as one bundle |
.channel | Installer/CLI | Tracks latest or pinned |
.download-base-url | Installer/CLI | Preserved into the staged release |
local-stack.json | Human operator | Copied unchanged into a staged update |
.runtime/ | Generator output | Regenerated from config and the installed release |
local-stack.json and generated environment files are mode 0600. The generator runs as the
invoking host UID and GID so mounted output does not become root-owned on Linux.
Bootstrap Boundary
The public installer performs the one-time host bootstrap:
- Validate macOS or Linux on AMD64 or ARM64 and require
curl,tar,awk,sed, andmktemp. - Verify Docker, the Compose plugin, and the selected Codex or API-key authentication prerequisite.
- Resolve
latestto an exact stablevX.Y.Z, or normalize the explicitly selected version. - Download the versioned archive and checksum over HTTPS.
- Verify the SHA-256 digest, reject traversal paths, and reject links or special archive entries.
- Extract into the installation root, write release-channel metadata, create the CLI symlink and install pointer, and initialize the machine-local configuration.
- Start the stack unless
--no-startwas selected.
Re-running the installer against a valid existing root delegates release movement to oblive update, then reinitializes without replacing the existing configuration. A directory that exists
but is not a valid Oblive installation fails closed.
Runtime Generation and Compose Invocation
The portable generator is bundled with the release, but it executes with the pinned Bun binary in the release’s semantic backend image:
host oblive script
-> pull <repository>:backend-vX.Y.Z
-> mount installation root at /oblive
-> run bun /oblive/lib/portable-stack.js as host UID:GID
-> write validated .runtime files
-> invoke Docker ComposeThis keeps the generator coordinated with the bundle without requiring a host runtime or making the backend image the owner of generator source. The CLI always invokes Compose with:
- project name
oblive; - the installation root as the project directory;
.runtime/compose.envas the environment file;compose.yaml; andcompose.codex-local.yamlonly whenauthModeislocal.
The fixed project name stabilizes container and named-volume identity across installation-directory
changes and bundle replacement. PostgreSQL, Redis, Garage, agent workspaces, and container-owned
Codex state use named volumes. Normal stop, restart, update, and uninstall operations omit
--volumes and therefore preserve that state.
prepare_runtime pulls the semantic backend image, initializes missing configuration, validates the
configured authentication source, and generates release-specific environment files. start then
pulls every selected image and runs compose up --detach --wait --remove-orphans. The migration
container is a one-shot dependency; PostgreSQL remains the durable owner of operational state.
Authentication Boundary
Local mode reads the configured host Codex auth.json only as an input to the dedicated
codex-auth-sync service. Chat and worker services mount container-owned Codex state; they do not
mount the host Codex home.
API-key mode resolves the configured host environment-variable name at invocation time. The CLI
passes that variable only to the generator container. The secret is absent from local-stack.json,
release metadata, and Compose interpolation output, and is materialized only in private generated
agent environment files.
oblive auth login is valid only for local mode. API-key mode supports status validation but never
owns a provider login flow.
Coordinated Update Transaction
start is intentionally version-stable. Only update can change the installed release.
Plain oblive update advances an installation whose .channel is latest. A pinned installation
requires --version vX.Y.Z or --latest. The update transaction is:
- Create a private staging directory next to the installed root.
- Resolve and download the requested stable bundle and checksum.
- Verify checksum, archive safety, embedded version, and semantic ordering.
- Reject a downgrade before touching the running installation.
- Copy
local-stack.json, channel metadata, and the download base URL into the staged root. - Run generation and
compose config --quietagainst the staged root. - If the old stack is running, stop it without removing volumes.
- Move the old root to a private backup and atomically move the staged root into its place.
- Restart only when the old stack had been running, then remove the backup after success.
Validation happens before downtime. If startup fails after replacement, the newly installed release remains active and the previous files remain in the printed backup directory. The CLI does not automatically roll back: the new startup may already have applied irreversible migrations. Docker volumes remain intact throughout the transaction.
An update to the already installed version changes the channel metadata when requested, but does not replace release files or restart services.
Command and Error Contract
The CLI uses three process-status classes:
| Status | Meaning |
|---|---|
0 | Help, a successful operation, or an intentional no-op |
1 | Runtime, dependency, authentication, validation, or I/O failure |
2 | Unknown command, invalid option, or invalid command usage |
Help is available through oblive, oblive --help, oblive help <command>, and <command> --help. Human-readable color appears only on a terminal and is disabled when NO_COLOR is set.
Keep errors actionable and send them to standard error. Preserve the service allowlist in logs so
user input cannot become an arbitrary Compose service or option.
Destructive Boundaries
Data removal must stay syntactically explicit:
reset --confirmstops the stack and removes named volumes but retains configuration and the CLI;- plain
uninstallstops services and removes the recognized CLI symlink without removing volumes or the installation root; and uninstall --purge --confirmremoves volumes, the recognized CLI symlink, the installation root, and its pointer.
Never make stop, restart, update, reinstall, or plain uninstall imply volume deletion. Never widen a recursive deletion target without resolving and validating the exact installation root first.
Change and Test Contract
Treat the bootstrap, archive, CLI, generator, Compose topology, semantic image tags, and release workflow as one compatibility boundary. A change to one may require coordinated updates to all of them.
Run these focused suites after changing the installed CLI:
.github/scripts/tests/deployment-bundle.test.sh
.github/scripts/tests/deployment-bundle-container.test.sh
.github/scripts/tests/portable-install.test.sh
bun test scripts/tests/portable-stack.test.ts
bun run --cwd sites/docs checkThe tests cover the exact archive contents and checksum, build-free Compose topology, POSIX shell syntax, generated-file ownership and permissions, install and PATH behavior, configuration preservation, help and exit statuses, authentication modes, semantic updates, downgrade rejection, and corrupt-download handling.
Use a bundle-only release only when existing application images remain compatible with the CLI,
generator, schema, and Compose change. Otherwise publish a full coordinated release. See
Container Releases for the publication and smoke-test boundary.