The handbook
TroubleshootingLocal developmentDeploymentProvision a new clientStandalone deploymentUpgradesArchitecture
Operate

Upgrades

How an AcasoOS deployment moves from one version to the next — on acaso's shared infrastructure and, especially, on a client's standalone instance. The goal is that an upgrade is always the same two moves: apply migrations, swap the image tag. No data export, no fork merge, no acaso engineer required on site. The in-repo ops counterparts are docs/runbooks/release.md and the Upgrades section of docs/runbooks/deploy-to-client-infra.md.

Versioning model

  • Each package (shared, datahub, agenthub, apphub) carries its own semver in pyproject.toml, with a per-package CHANGELOG.md generated from Conventional Commits.
  • An umbrella git tag v<MAJOR>.<MINOR>.<PATCH> snapshots a tested combination of package versions. The container image for a release carries the same tag.
  • Two spellings, on purpose: bare semver (0.2.0) inside the app and its config (pyproject.toml, the RELEASE_VERSION env var, Helm releaseVersion); v-prefixed (v0.2.0) for git tags, image tags, and Helm image.tag.

RELEASE_VERSION drives the version badge on each service's /docs Swagger UI and the Sentry release tag — it is how you verify which build is live. If the badge disagrees with the image tag you deployed, the env var didn't move with the image.

What a release contains

  • The container image, built from the tagged commit — including every namespaced bespoke directory (templates/custom/*, agenthub/custom/*), compiled and tested together with the core. Clients never receive code that wasn't part of the tested release.
  • Alembic migrations, append-only and re-runnable. CI's make migration-check proves upgrade → downgrade → upgrade works for every migration. Breaking schema changes don't ship without an ADR and an explicit migration path.
  • Release notes and per-package changelogs, generated from Conventional Commits between tags. Anything a standalone operator must do beyond the standard two moves (a new required env var, a renamed setting) is called out there explicitly.

Releases are cut from release/v<X.Y.Z> branches per docs/runbooks/release.md: version bumps across every source-of-truth file, staging smoke test, merge to main, tag, CI image build and publish, backmerge to develop.

Upgrading the shared deployment

acaso operates this continuously: the standard CI/CD path deploys the tagged image, and RELEASE_VERSION on the deployment environment moves to the bare semver at the same time as the image tag. Tenants ride along; per-tenant configuration (overrides, prompts, bindings, scopes) is data, so it survives upgrades untouched.

Upgrading a standalone instance

The client SRE runs this on their own cadence, with no acaso involvement:

  1. Read the release notes for every version between the running one and the target. Confirm whether any env var changes apply.
  2. Snapshot the database. The backup is the rollback path of last resort.
  3. Pull the new image: docker pull <REGISTRY>/acaso-os:v0.2.0 (or build it from the tag: git checkout v0.2.0 && docker build ...).
  4. Apply migrations BEFORE swapping any service, using the NEW image and the privileged database role:
bash
docker run --rm \
  -e DATABASE_URL="postgresql+asyncpg://acaso:...@<DB_HOST>:5432/acaso_os" \
  <REGISTRY>/acaso-os:v0.2.0 alembic upgrade head

Migrations are written so the previous app version keeps working against the upgraded schema — that is what makes the next step a rolling update instead of downtime.

  1. Swap the image tag on each service (DataHub API, AgentHub API, worker) — ECS task definition, K8s Deployment, or helm upgrade --set image.tag=v0.2.0. Old and new tasks may coexist briefly.
  2. Move RELEASE_VERSION to 0.2.0 (bare semver) on the services, together with the tag.
  3. Verify: /health and /ready return 200; the /docs badge on both APIs reports 0.2.0; ./scripts/smoke_test.sh passes; one agent runs end-to-end; the worker drains an ingest event.

Skipping versions is supported — alembic upgrade head replays every migration in order — but read each skipped release's notes.

The compatibility contract

What makes the two-move upgrade safe, and what each side must hold up:

acaso guaranteesThe client/operator guarantees
Migrations are append-only, re-runnable, and never break the previous minor without an ADR + explicit calloutMigrations run before the image swap, never after
Bespoke namespaces (templates/custom/<slug>/, agenthub/custom/<slug>/) are never modified by core changesAll bespoke changes go through the monorepo, never patched onto a deployment
Env var renames and new required config are called out in release notesRelease notes are read before upgrading; RELEASE_VERSION moves with the tag
Every release is tested as one unit — core plus all bespoke code — by the full CI gate setThe instance runs tagged releases only, never branch builds

Rollback

  1. Revert services to the previous image tag (and RELEASE_VERSION). Because migrations are backward-compatible by policy, the old code runs against the new schema.
  2. Only if a migration itself is the cause, run the previous version's alembic downgrade -1 — rare by design, and the reason migration-check exists.
  3. Open an incident per docs/runbooks/incident-response.md and report it to acaso so the release gets fixed for everyone.

Cadence

Track every MINOR within a sprint or two; apply PATCH releases promptly — security fixes ship as patches. The further an instance trails, the more release notes pile up for one jump; the process supports it, but short hops keep each upgrade boring. acaso notifies standalone clients on every release with the changelog and any callouts.

PreviousOperateStandalone deploymentNext OperateArchitecture