Download Obol 0.1.0
for Linux AMD64 or ARM64. The release includes pinned images, Compose, Helm,
an offline archive, license notices and signed checksums.
NOTICE; third-party
components retain their own licenses. The application repository remains
private.
What you operate, and what Obol offers
Managed hosting (Obol Cloud) and contracted Enterprise support are available
separately if you want someone else to carry the operating responsibility.
They are offered, never required: nothing in a Free Self-hosted installation
stops an allowed call to ask you to upgrade.
Running the free distribution establishes no support relationship with Obol.
A Free Self-hosted installation reports itself as customer-operated with no
contracted support, and it never displays an Enterprise operations claim.
Honest limits
- Single host, Docker Compose. The supported topology is one Linux host running the release’s Compose file. Kubernetes, high availability, VPC delivery and support are contracted separately. The offline bundle carries the same release images for hosts without registry access.
- No SLA. There is no uptime, response-time, or recovery commitment.
- One installation owner. The first release enrolls a single local owner through a one-time setup token by default. Alternatively, select Clerk during installation and supply your own Clerk application keys.
- You hold the keys. The installer generates the encryption identity, signing keys, and datastore passwords on your host. Back them up with the database — Postgres alone cannot recreate a lost key, and Obol cannot recover them for you.
- Built-in assistance is absent unless you configure its credentials yourself; those surfaces render not evaluated rather than borrowing a credential.
- Obol Workers and built-in tools are Obol Cloud services. They are not part of Free Self-hosted. Control refuses Workers and configures no built-in tools on a self-hosted installation, and it offers no prepaid balance there (ADR-0215).
Configure AI providers and the copilot
After signing in, open Settings → Configuration (/dashboard/settings/configuration). First run opens this page; it brings
provider keys, copilot selection, setup progress and public server addresses
together. The copilot also links directly to its AI configuration section.
- Add an OpenRouter, OpenAI, Anthropic, or compatible API key and an exact model ID from your provider account. Keys are write-only and vaulted; your provider bills you directly.
- Publish a workspace policy allowing that model. In Copilot model, select the published connection, review the monthly provider budget, request rate, output limit and timeout, and choose Enable copilot. Saving another model applies to new turns. Only a workspace owner can change copilot access.
- Optionally connect Jev decisions using an OpenRouter or direct TypeSafe key. Jev returns typed decisions rather than chat replies, so it has its own key form and is excluded from the copilot chat selector. These are ordinary governed decision-tool connections for your agents; connecting one does not configure the installation’s built-in evaluator.
typesafe/jev-1.13; the direct TypeSafe
connector pins jev-1.13.0. A compatible chat provider needs an HTTPS base URL
ending in /v1. A pending connection is shown as publishing or needing a key;
refresh its status before enabling the copilot.
Under Server addresses, enter your dashboard, Control and gateway HTTPS
origins and download obol-configuration.json. Copy it to the installation
and run ./obol configure obol-configuration.json, then ./obol start and
./obol doctor. The download contains only public addresses; credentials and
private keys remain in the installation. Downloading alone changes nothing
on the running server.
Install with an AI agent
You can have a coding agent that runs on the target host, such as Claude Code, Codex, or Cursor, do the install. Copy one of the prompts below and give it to the agent. Both keep it to the documented steps: it may not read your keys, build from source, delete data, or expose ports, and it hands the owner setup token back to you.- One-line installer runs the same
install.shas the install command. The script checks the archive and bundle checksums; it does not verify the publisher signature. The tryobol.dev home page offers this prompt with a copy button. - Verified bundle downloads the release archive yourself and verifies the
Cosign publisher signature before anything runs. Replace
<RELEASE_ARCHIVE_URL>with theobol-selfhost-<version>.tar.gzlink from the release page.
- One-line installer
- Verified bundle
Install Obol Free Self-hosted on this machine with the one-line installer.
The setup token gives whoever holds it the installation owner role until the
token is used or expires. When the agent hands off, run
./obol-selfhost setup-token (or obol setup-token in the install directory,
for the one-line installer) yourself. That replaces the token printed during
init, which the agent saw.Install
Install Docker Engine and its Compose plugin first. On the host, run:/opt/obol, retains its files in
releases/<version>, generates secrets, pulls the pinned images, runs migrations,
and starts the stack. Run as the directory’s owner, or choose a writable directory:
--no-dashboard. Gateway, Control,
worker, migrations, Postgres and Redis still run. Operator API authentication
remains required; use the local owner setup API or configure Clerk before managing
workspaces. No Rust, Node, Python or age installation is required on the host:
age-keygen runs inside the release’s control image during initialization.
The equivalent manual installation follows.
1
Download and verify the release
Download the A checksum proves you have the bytes the release listed; it does not prove who
published them. The signature authenticates the public Obol distribution
workflow and all release bytes;
obol-selfhost-<version>.tar.gz bundle and its .sha256 file
from the public release page,
along with RELEASE_SHA256SUMS and RELEASE_SHA256SUMS.sigstore.json. With
Cosign installed, verify the publisher signature and downloaded files before
running anything:release.json binds the image digests. It does
not expose or independently prove the private build process. Every file you
intend to use must appear in the signed index and report OK.
The bundle contains the installer script, a Compose file that pins every
application image by digest, release.json, notices, and a short guide. No
source checkout, compiler, or registry login is needed.2
Initialize
init checks Docker, architecture, ports, resources and permissions, then
generates fresh installation secrets and a one-time owner setup token, writes
configuration with restricted permissions, and refuses to overwrite existing
key state. It records the Docker Compose project the installation runs under
as COMPOSE_PROJECT_NAME in .env (obol-selfhost, or init --project NAME)
and refuses a name another stack on the host already uses; every later command
passes that name explicitly and ignores a COMPOSE_PROJECT_NAME exported in
your shell. The only secret it prints is the one-time owner setup token, shown
once when it is generated and never stored (only its hash is kept); no other
secret value is printed.3
Start
start pulls the release’s exact image digests, runs database migrations,
and waits for the services to report healthy. It never builds from source.
Product ports bind to 127.0.0.1 by default. It points you at /setup only
while owner setup is open, and at the sign-in page once the owner exists.The containers have no restart policy: after a host reboot or a Docker
restart, run ./obol-selfhost start again (or from a systemd unit or an
@reboot cron entry).4
Claim the installation owner
Open the dashboard at the loopback address
init printed and go to /setup.
Enter the setup token, your email, and a password. The token is single-use
and expires; once the owner exists, setup is closed. If it expires first, run
./obol-selfhost setup-token on the host for a new one. Setup creates your
owner account and a dev workspace with no policy permits of its own. Until
you publish a policy, the built-in default refuses every tool call and permits
only tool listing and model completions, which each key’s model allowlist
still bounds.5
Make a first governed read
Follow the first-run guide on the dashboard home: connect one of your own
credentials (a model provider key, or a native connection such as GitHub),
publish a narrow policy, and issue an
ob_test_ key. Point your client at the
local /v1 or /mcp endpoint the guide shows and make one read. Then open
its receipt — on a native route it can be gateway_observed; the receipt
always names the evidence class its route earned — and try one out-of-scope
call to see it refused before it reaches the vendor.Operating reference
The release Compose file runs the same binaries as managed hosting. Fly.io is the internal managed-hosting path. Source builds belong in the contributor Compose reference.What runs
Every application image is published forlinux/amd64 and linux/arm64 under
ghcr.io/tryobol/gateway, ghcr.io/tryobol/control, and ghcr.io/tryobol/web,
with the same version tag. Release Compose pins their digests and the digests of
Postgres 16 Alpine and Redis 7 Alpine. It contains no build instructions.
Control, gateway and web bind
${OBOL_BIND_ADDRESS:-127.0.0.1}; Postgres and
Redis are loopback always. Widen OBOL_BIND_ADDRESS only behind a TLS reverse
proxy. The default render runs the first seven services; landing and the Obol
Workers services are Compose profiles that start only when named.
The control image is one image with three roles selected by the container
command: api, worker, and migrate. apps/control/docker-entrypoint.sh
dispatches them.
Boundaries the topology enforces
- The gateway has no
DATABASE_URL. Postgres never sits on the streaming path; the gateway reads snapshots from Redis and falls back to control’s/internal/v1/*. - Only the gateway can unwrap a credential. No other workload receives a
vendor secret or key-encryption key, and no target sets
OBOL_CONNECTORS_URL. Were a worker deployment added, it would need a private-only address and an ingress policy admitting the gateway alone — and it would today receive the vendor credential over ADR-0014’s hand-off, which is why one has not been. - Control’s
/internal/*is never routed by the public edge. Under Compose the whole control port is published onOBOL_BIND_ADDRESS(loopback by default), so put control behind a reverse proxy that allows only/api,/oauth,/.well-known, and/healthzwhen it is reachable from outside the host.
Required configuration
The installation’s.env contains its runtime configuration. Keep it mode
0600 and back it up together with secrets/age.key and the database.
Dashboard authentication and public URLs are read at runtime. Changing the
Clerk key or domain requires restarting the service, never rebuilding an image.
The browser receives only the explicit public configuration; secret keys stay
server-side. In Clerk mode, missing identity configuration fails closed.
Compose refuses to start without
POSTGRES_PASSWORD, REDIS_PASSWORD,
OBOL_SERVICE_JWT_SECRET and OBOL_APPROVAL_REPLAY_KEY; none has a default.
Outside OBOL_ENV=dev, control also refuses to start without the ADR-0131
service-signing key set (.env.example names it).
Key-encryption key
OBOL_KEK_PROVIDER selects how vaulted credentials are wrapped.
age-keygen and writes its public recipient into .env. The private identity is mounted as a Compose secret into the gateway
only, at /run/secrets/age.key. Control gets OBOL_AGE_RECIPIENT and can
therefore seal a credential but never open one. Connectors and web receive no key
material at all. See vault and key management.
Optional configuration
Leaving the Stripe variables blank is a supported self-host posture: usage is
still recorded, and only delivery to Stripe waits. See
metering.
Database migrations
migrate runs alembic upgrade head and exits. Both control and
control-worker depend on it with the service_completed_successfully
condition, so no application process starts against an un-migrated schema.
Control also has a /healthz healthcheck the gateway waits on before it starts.
The Kubernetes chart applies the same rule differently — a revision-named Job
plus an init container that waits for the image’s full Alembic head set
(ADR-0043). See Helm.
Upgrading
From the installation directory:--dir if you chose a custom directory. Without a version,
./obol upgrade downloads the latest published release. The upgrade verifies
checksums and the declared upgrade floor, takes a Postgres dump and Redis/key
backup, pulls the new digests, stops product services, runs migrations, then
starts Control and the worker and waits for service health. Failed migrations
stop the upgrade. Keep the backup; an older image cannot undo a schema change.
/startupz checks the gateway’s first-boot dependencies. /readyz is traffic
readiness and returns 503 until the first workspace policy snapshot is published.
After setup and policy publication, require it to return 200 before sending traffic.
The installer never invents a permissive policy to make this probe pass.
What carries across upgrades and restores
These carry across upgrades and restores by design:- The Compose project. An installation made before
COMPOSE_PROJECT_NAMEwas recorded is refused with a one-line fix rather than guessed at: add the project it runs under (docker compose ls; earlier releases usedobol), for exampleprintf 'COMPOSE_PROJECT_NAME=obol\n' >> .env, then run./obol-selfhost doctor. - Restore on the same host. Restoring a backup next to the installation it
came from (restore testing) needs its own project and ports, or it collides
with the original:
./obol-selfhost restore <dir> --project NAME --ports-offset N(or explicit--web-port,--control-port,--gateway-port,--postgres-port,--redis-port). This rewrites only the restored copy’s ports and public URLs and is for restore testing only. - Login lockouts. Five failed sign-ins in 15 minutes lock the address for
a browser the owner has not signed in from; a browser holding a live device
credential is counted on its own bucket instead (ADR-0165). Lockouts live in
Redis, which is persisted and backed up, so a restart, upgrade or restore
does not clear them. They expire after the window, and
./obol-selfhost reset-owner-password --email <owner>clears them at once.
Moving a Clerk-mode installation to local sign-in
A release-bundle installation can switch from Clerk to local sign-in while keeping its data. Existing workspaces belong to Clerk users; local owner setup creates a new owner and a newdev workspace. The host operator can then grant
that owner access to existing workspaces with adopt-workspace (ADR-0166).
These commands run in the installed release directory. For an older, custom
source deployment, first plan the transfer of its existing database, Compose
project and encryption keys to the release bundle. Keep a verified backup;
do not initialize an empty replacement over existing data.
1
Back up and upgrade the release
2
Switch the mode and mint a setup token
In the installation’s protected
.env, set OBOL_OPERATOR_AUTH_MODE=local,
OBOL_CLOUD_HOSTED=false and OBOL_PUBLIC_SIGNUP_ENABLED=false, then run:setup-token records the token’s hash and expiry, recreates Control, and shows
the one-time token. It refuses to reopen an installation already claimed by a
local owner. start applies the configuration to the remaining services.3
Claim the owner
Open
/setup, enter the token, and create the owner.4
Adopt the existing workspaces
adopt-workspace runs only on a claimed, local-mode, self-hosted installation
and only for the owner’s email. It adds the owner’s owner membership and
never removes or changes anyone else’s, never contacts Clerk, is safe to run
twice, and writes one selfhost.workspace_adopt audit row per workspace with
the host as the actor. --include-group on an environment group’s anchor
adopts its other environments too; without it they are listed and left alone.Kubernetes and offline installs
Each release also containsobol-<version>.tgz, helm-values.yaml, and
obol-bundle-<version>.tar. The chart uses the same application image digests.
Download the chart and release values together, then supply your own datastore,
key-encryption, identity, networking and service-signing configuration as described
in Helm:
pull_policy: never.
For an upgrade, load the new archive’s images first and run the installed
./obol upgrade /absolute/path/to/new-bundle/loaded-amd64 (or loaded-arm64). Restore into the
matching offline release directory. Optional Bitnami database images and Obol
Workers are not included; Kubernetes operators provide their own Postgres/Redis
and import application images into their registry or nodes. External model and
tool calls still need access to their vendor endpoints.
Enterprise features
Core gateway, policy, receipts, credential encryption and KMS remain available without a license. Contracted support, SCIM, audit streaming/export and BYOA use a signed offline license supplied with the agreement. No license or private signing key is stored in the repository or public images. SetOBOL_ENTERPRISE_LICENSE to the issued token in the protected .env, and
OBOL_LICENSE_VERIFICATION_KEY to the issuer’s base64url Ed25519
public key supplied with it. Control checks the signature, expiry, feature set
and workspace scope at startup and when protected capabilities are used. Existing
verified Enterprise grants are still required. An absent or expired optional
license leaves core operation running and denies premium capabilities.
For a deployment that must refuse startup without BYOA entitlement, set
OBOL_LICENSE_REQUIRED_FEATURES=byoa. Helm exposes this as
enterprise.byoa=true, with enterprise.licenseSecret naming an existing Secret
containing OBOL_ENTERPRISE_LICENSE and enterprise.verificationKey supplying
the public issuer key. Do not commit license tokens to values files.
Data and state
Redis runs with
--appendonly yes and --maxmemory-policy noeviction. The
eviction policy is not incidental: snapshots and idempotency records must not be
evicted under memory pressure.
Back up pgdata. Redis is a cache and a stream buffer in front of Postgres, but
usage events in flight are only in Redis until the worker drains them, so drain
before a destructive Redis operation.
Next steps
Compose reference
Release commands and the separate contributor Compose workflow.
Helm
The Kubernetes path, for contracted deployments.
Invariants
The rules the deployment is shaped to enforce.
Telemetry
Metrics, traces, and where they go.