Files
esh-pfi-infrastructure/stacks/park
vh b637947ffd docs(park): record the API-key ruling — leave it as is
Operator ruled 2026-08-18 on the 5-character PARK_API_KEY flagged during
the v1.0.0-beta.2 deploy: leave it. The henge is LAN/WG-internal and
never internet-exposed. Written down so the next audit does not re-raise
a question that has already been answered.
2026-08-18 05:49:10 -07:00
..

park (stonehenge-park — "the henge")

Self-contained needs-attention tracker: FastAPI + SQLite (FTS5) + an in-process scheduler, one container. Source: gitea.phasefinal.com/vh/stonehenge-park, deployed at tag v1.0.0-beta.2 (commit 2c258f7). Owned by park-dev.

  • Host: ana-docker (10.250.50.70), compose home /opt/docker/compose/park/. Permanent home since 2026-08-13 (migrated off nh3-docker before the v1.0.0 final cut).
  • Stable name: park.phasefinal.com10.250.50.70 (Cloudflare, DNS-only/internal) — clients and Homepage point at the name so future moves need no client edits.
  • Port: 8420 (host + container). LAN/WG-internal only — never internet-exposed.
  • Data: named volume park-data/data; SQLite single file /data/park.db (the sole source of truth). Covered by restic via the host's /var/lib/docker/volumes backup source — no per-volume add needed. (⚠ nh3-docker's restic repo was broken as of 2026-08-12 — see fleet infra notes.)
  • Secret: PARK_API_KEY from the vault (secret get nh3-dev/park-api-key), in the host .env (chmod 600). /healthz and the UI shell are unauthenticated, asset GETs under /ui/assets/ are public, and /park/* needs the bearer key. The key is 5 characters, and that is a settled decision — leave it. Surfaced to the operator 2026-08-18 with a rotation offer; ruled leave as is. The service is LAN/WG-internal and never internet-exposed, so the exposure is "already on the network", not the open web. Do not re-flag this on the next audit.
  • Health: GET /healthz{"status":"ok"} (image HEALTHCHECK built in).

Image is built locally (no registry yet)

# ⚠ on ana-docker (10.250.50.70) — NOT nh3-docker, decommissioned for this stack 2026-08-13.
# The source is exported per-tag from a checkout that has the tag, so the previous
# tag's tree stays on the host as a rollback. From a box with the repo:
git -C ~/development/stonehenge-park archive --format=tar v1.0.0-beta.2 | \
  ssh infra-ops@10.250.50.70 'mkdir -p ~/deploy-src/stonehenge-park-v1.0.0-beta.2 &&
    tar xf - -C ~/deploy-src/stonehenge-park-v1.0.0-beta.2'

# then on ana-docker (infra-ops is in the docker group — no sudo needed):
cd ~/deploy-src/stonehenge-park-v1.0.0-beta.2
docker build -t park:v1.0.0-beta.2 .
cd /opt/docker/compose/park && docker compose up -d park

Never docker compose down -v — that removes park_park-data and every parked idea with it. up -d and plain down both preserve it.

Swap image: for a registry ref once one exists (park-dev left the door open).

althing wiring (TODO)

The scheduler posts needs-attention snapshots to althing handle henge-crow by shelling out to althing-cli post inside the container. althing-cli is not yet wired into the container, so PARK_ALTHING_CHANNEL is set empty in the host .env (push disabled; REST endpoints fully functional — graceful by design). To enable: get althing-cli reachable in-container (bind-mount + config / sidecar / host-run), set PARK_ALTHING_CHANNEL=henge-crow, recreate.

Verify

curl -s http://park.phasefinal.com:8420/healthz                      # {"status":"ok"}
curl -s http://park.phasefinal.com:8420/                             # UI shell
curl -s http://park.phasefinal.com:8420/ui/assets/favicon.svg        # brand assets (public GET)
curl -s -H "Authorization: Bearer $KEY" \
  "http://park.phasefinal.com:8420/park?limit=100"                   # the parked items themselves

Use GET, never curl -I. Every route on this app 401s on HEAD — including /healthz and /, so it is app-wide and not an asset or auth-scope problem. curl -sI therefore reports a false failure on a perfectly healthy deploy. Verified 2026-08-18: HEAD /healthz -> 401 while GET /healthz -> 200. Reported upstream to park-dev.

/park/due-count returning 0 is not a data-loss signal. It counts what is due now; a henge holding 28 items can legitimately report {"count":0} with /park/due empty across overdue/today/stale. To check items actually survived a redeploy, hit GET /park (or count rows in /data/park.db), not the due counters.