Files
esh-pfi-infrastructure/stacks/albok-service
vh d3958655c1 feat(albok-service): deploy the fleet knowledgebase service on nh3-docker
albok-service 0.1.0 (vh/albok 0b37431), image pfi/albok-service pinned by
digest, published on host port 8392 because 8390 is the post office.
Host prep playbook creates the fixed ids (albok 1500, albok-read 1510,
albok-personal 1511) and the local store/private roots; the container
gets a mounted /etc/group and group_add so the service can resolve and
chgrp its wing dirs. The config carries a LiteLLM key scoped to
qwen3-embedding and lives outside the deploy-synced conf dir. DNS name
albok.nh3.internal.
2026-10-02 17:56:34 -07:00
..

albok-service — fleet knowledgebase service (nh3-docker)

The FastAPI process that owns Albok's buildings-and-wings store (vh/albok, packages/albok-service, contract docs/contracts/unit2_service.contract.md). It is the only writer of the store. Deployed 2026-10-02 at albok-dev's request (operator-approved).

  • URL: http://albok.nh3.internal:8392 (= http://10.100.50.40:8392). Container port 8390. ⚠ Host port 8392, because 8390 on nh3-docker is the althing post office.
  • Image: gitea.phasefinal.com/pfi/albok-service:0.1.0 @ sha256:9ae2c94e… (pinned in compose.yaml), built from vh/albok 0b37431 (tag v0.1.1; albok core 0.1.1 inside).
  • Health: GET /health (no auth). ⚠ 0.1.0 reports "status": "degraded" even when everything is fine: health.py treats canary values other than ok/healthy/ready as dead, and the canary reports "alive". Reported to albok-dev. Read the fields, not status. The Docker healthcheck only checks for HTTP 200.

Pieces and where they live

What Where
store root (one git repo per wing) /srv/albok/store — local ext4, albok:albok 0711
private root (lease, journal, tokens.db, per-wing chroma) /srv/albok/private — local ext4, albok:albok 0700, single-attach
config (holds the embedder key) /srv/albok/etc/albok.yaml — root:albok 0640; rendered from conf/albok.yaml.template
container /etc/group /opt/docker/conf/albok-service/group (= conf/group)
compose /opt/docker/compose/albok-service/compose.yaml

Identities (fixed numeric ids, created by playbooks/albok-service-host.yaml): user and group albok 1500:1500 (the image's user), read groups albok-read 1510 (building fleet) and albok-personal 1511 (building personal). Reserve 1512+ for restricted wings (agent-feedback, vault) and add each to the host, conf/group and group_add.

Why the group file and group_add: the service resolves group NAMES with grp.getgrnam() inside the container and chgrps wing dirs as uid 1500. Without the names in the container's /etc/group it reports "group does not resolve", and without membership the chgrp is refused. Verified: wings came up drwxr-s--- albok:albok-read / albok:albok-personal, drift 0.

Secrets (vault): albok/litellm-key, a LiteLLM key scoped to qwen3-embedding only (alias albok-service; verified 200 on embeddings, 403 on any other model); albok/bootstrap-admin-token, the first-start admin token (also at /srv/albok/private/bootstrap-admin-token, 0600).

Backups: nh3-docker is VM 100 on nh3-pve, in the nightly 21:00 vzdump (snapshot mode → pbs-ana), so /srv/albok is covered whole-VM. No file-level restic job for it.

Deploy / redeploy

# 1. host prep (idempotent)
scripts/elway infra-ops@10.100.50.40 --playbook playbooks/albok-service-host.yaml
# 2. config: render the template with the vaulted key (never commit the rendered file)
umask 077; secret get albok/litellm-key | tr -d '\n' | python3 -c "import sys; k=sys.stdin.read(); \
  open('/tmp/albok.yaml','w').write(open('stacks/albok-service/conf/albok.yaml.template').read().replace('__ALBOK_LITELLM_KEY__', k))"
scripts/elway infra-ops@10.100.50.40 --upload /tmp/albok.yaml:/srv/albok/etc/albok.yaml:0640 --sudo --owner root:albok
# then delete /tmp/albok.yaml by its literal path
# 3. compose + conf, then start (root holds the registry login on nh3-docker)
scripts/deploy-stack.sh nh3-docker albok-service
ssh infra-ops@10.100.50.40 'cd /opt/docker/compose/albok-service && sudo docker compose up -d'

Image rebuild (from a clean archive, never a working tree):

git -C ~/development/albok archive <sha> | tar -x -C <scratch>
cd <scratch> && docker build -f packages/albok-service/Dockerfile -t gitea.phasefinal.com/pfi/albok-service:<ver> .
secret get nh3-dev/.config/claude-bot/gitea-token-sdkops | docker login gitea.phasefinal.com -u claude-bot --password-stdin
docker push gitea.phasefinal.com/pfi/albok-service:<ver>     # then pin the new digest in compose.yaml

Viewers (read-only access to the store)

Mount /srv/albok/store :ro, group_add the read group's numeric gid (1510 / 1511), set GIT_OPTIONAL_LOCKS=0, and add safe.directory for the path as the viewer sees it. Nothing else may mount the store read-write.