Files
esh-pfi-infrastructure/stacks/althing-chamber/README.md
T
vh 91d5417b0b althing-chamber: scaffold deploy stack on ana-docker
Two-service compose (chamber + forseti sidecar daemon) sharing a single
SQLite store via bind-mount under /opt/docker/conf/althing-chamber/data.
eventbus.bridge_from_db is the cross-process glue — forseti's commits
reach chamber's SSE subscribers via the bridge.

Pattern matches task-board's build-on-host deploy:
  - elway playbook clones vh/althing into /opt/docker/build/
  - docker build -t althing-chamber:local . (no registry)
  - playbook uploads compose + seeds .env one-time, brings both
    services up, polls /health
  - Gitea Actions workflow lives in vh/althing; reference copy here.

Internal tooling — host port 7881 (chamber's default of 7878 collides
with task-board). LAN-direct, no Traefik. Container always listens on
8000 internally.

Scaffold will fail to bring the chamber container up healthy until
galdrabok-side commits land:
  - Dockerfile at vh/althing repo root (two-stage: uv-bookworm-slim
    build → python:3.12-slim runtime, locked per open_questions §2
    of the v1 contract).
  - GET /health endpoint on the chamber app (200, no DB read).
  - ALTHING_BIND / ALTHING_PORT env-var support in
    core.cli.chamber_serve / core.chamber.cli (env > config.yaml >
    defaults precedence).

Coordinated via althing thread 01KRMAK7RD7TP6C8DF4KXV31RT.
2026-05-14 15:54:19 -07:00

4.4 KiB

althing-chamber

Web UI + moderator daemon for the althing inter-agent message bus. Two services share a single SQLite store via a bind-mount; the chamber serves FastAPI/HTMX, the forseti daemon runs the moderation + curation loops. Cross-process glue is eventbus.bridge_from_db polling the same DB both processes write.

Server: ana-docker URL: http://10.250.50.70:7881 (configurable via .env) Upstream repo: vh/althing Image: althing-chamber:local — built on the host from the git repo by the deploy playbook. Not pulled from a registry.

Services in this stack

Container Role Port Healthcheck
althing-chamber FastAPI/HTMX web UI; SSE subscribers; /health endpoint host 7881 → container 8000 python urllib /health
althing-forseti Moderator + curator daemon; writes events that chamber's bridge picks up — (no HTTP) none (process-up signal only)

Both use the same ${ALTHING_IMAGE}; the command: line in compose picks which entrypoint (althing-chamber vs althing-forseti) runs in each container.

Deploy

Two paths — automated (preferred) and manual (escape hatch / first-time).

Automated (Gitea Actions, push-to-main)

The vh/althing repo ships .gitea/workflows/deploy.yaml. Every push to main + manual workflow_dispatch triggers the elway playbook below pinned to the triggering commit SHA. A reference copy of the workflow lives next to this README at gitea-workflow-deploy.yaml.example; the canonical source is in the vh/althing repo. The example header lists the two repo secrets required (DEPLOY_SSH_KEY, MGMT_REPO_TOKEN).

Manual (elway from a workstation)

# First deploy (or update to latest main)
scripts/elway ana-docker --playbook playbooks/deploy-althing-chamber.yaml

# Pin to a specific ref (tag, branch, or commit SHA)
scripts/elway ana-docker --playbook playbooks/deploy-althing-chamber.yaml --var ref=v0.1.0

Path layout (on ana-docker)

Host path Container path Purpose Restic?
/opt/docker/build/althing-chamber/ git checkout used as docker build context excluded
/opt/docker/compose/althing-chamber/ compose.yaml + .env included (via /opt/docker)
/opt/docker/conf/althing-chamber/data/ /app/data SQLite + state for BOTH services included

Network model

Internal tooling, LAN-only. Chamber's container port 8000 is published on the host at 0.0.0.0:7881 (configurable via ALTHING_BIND / ALTHING_PORT); access is direct via http://10.250.50.70:7881. No Traefik, no TLS terminator, no public hostname.

Forseti has no port — it's a daemon. The two services communicate only via the shared SQLite file under the ${ALTHING_DATA_DIR} bind-mount; no docker network coupling beyond compose's default bridge.

Env-var contract

Var In env? In ~/.althing/config.yaml? Notes
ALTHING_ROOT ✓ (set in compose) n/a Overrides the default ~/.althing/ dir. Container sets /app/data.
ALTHING_DB ✓ (set in compose) n/a Explicit SQLite path. Defaults to ${ALTHING_ROOT}/althing.db.
ALTHING_BIND ✓ (deploy-time) also accepted Bind address for the chamber HTTP server. Container always sets 0.0.0.0 internally.
ALTHING_PORT ✓ (deploy-time) also accepted Chamber listens here internally (always 8000 inside the container).

Env-var support for ALTHING_BIND / ALTHING_PORT was added on the galdrabok side as part of the container-deploy cycle (env > config.yaml

defaults precedence).

First-deploy sequence (when galdrabok's Dockerfile lands)

  1. Generate a deploy keypair on ana-docker (private stays on host; pubkey lands in ~lkraven/.ssh/authorized_keys).
  2. Wire two secrets in vh/althing Actions settings:
    • DEPLOY_SSH_KEY — the private key from step 1.
    • MGMT_REPO_TOKEN — Gitea PAT with read:repository on this repo, used by the workflow to clone the management repo for the playbook.
  3. Push to vh/althing's main branch (or trigger workflow_dispatch); the Actions runner clones both repos, configures SSH, runs scripts/elway ana-docker --playbook playbooks/deploy-althing-chamber.yaml --var ref=$SHA.
  4. Playbook: clones into /opt/docker/build/althing-chamber, builds the image, uploads compose + .env (one-time seed), brings both services up, polls /health until 200.