Repository navigation
feat(sensitive): Actual behind Caddy, claimed before it answers, decided by ADR-0062 (#142) - #733
Merged
Merged
Conversation
…ded by ADR-0057 (#142) Actual joins stacks/sensitive as the household's budget. It is the first service beyond ADR-0008's nine, so ADR-0057 decides it: Actual rather than Firefly III, because it is one container on SQLite and keeps working when the server is down. It is authored and validated here. Deploying it on trinity is the remaining step. Actual has no password setting. A fresh server belongs to whoever sets the first password, and the image's reset script needs a terminal. scripts/seed-actual-password.sh claims it from SOPS (ACTUAL_SERVER_PASSWORD), inside the pinned image with --network none, before its first start; `make up` runs it every time, and on a claimed server it only checks. The value never enters the container's environment or .env. Proved on throwaway volumes: fresh, already claimed, a live check, a wrong password. Measured on 26.9.0-alpine: - runs as the image's own uid 1001, read-only, no capabilities, and docker diff is empty - boots with no network - account.sqlite is in rollback-journal mode - 279 MiB idle, 329 MiB peak - five failed logins per client per 15 min - header login is refused In password mode every device shares one session token, and a changed password does not revoke it. Deleting the sessions rows with the service stopped does; the stack README has the command, proved the same way. Also: - actual-data joins the nightly set (sentinel ./server-files/account.sqlite) and restore-volumes.sh's uid table - the restore runbook checks the claim - password login only, with Caddy as the one trusted proxy - no bank sync - a link-only Homepage tile - a CI hardened boot - ADR-0023 class Durable; ADR-0022 no second factor, as pointer notes Refs #142 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…0058 Miniflux (#147) landed first as the first tier extra, so Actual is the second: ADR-0057 → ADR-0058 in every Actual reference, and "first beyond ADR-0008's nine" becomes "second, after Miniflux". Both services kept in every conflicted file (compose, Caddyfile, secrets template, README, runbooks, security.md, ADR-0022's note, roadmap, changelog, restore uid table). Counts corrected for both services, including ones main left stale: - 17 services and 18 secrets - 10 names, 9 of them host overrides - 15 volumes, 12 archived, 7 not rebuildable - 58 ADRs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…R-0059 Actual's ADR moves again, 0058 → 0059, in every Actual reference; HomeBox's own ADR-0058 references are untouched. The changelog keeps both entries, Actual's first. README's count is 59 ADRs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gerrrt
enabled auto-merge (squash)
September 29, 2026 16:19
Memos (#145) landed as the second tier extra, so Actual is the third, and its record moves to ADR-0060. Every Actual reference is renumbered; Memos' own ADR-0059 references are untouched. Both services are kept in every conflicted file: the Homepage tiles, the README, the restore checks (Actual's is now step 8), the host-override names, and the notes in ADR-0022 and security.md. This merge also brings in #734, which gives Miniflux's Postgres its own cluster directory. With it, the sensitive backup inventory passes again. Counts recounted from the files: - 18 services, 18 secrets - 11 names, 10 of them host overrides - 16 volumes, 13 archived, 8 not rebuildable - 60 ADRs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… extra Mealie (#146, ADR-0060) landed as the third tier extra. Actual is now the fourth and the eleventh service in the compose header; its record was renumbered to ADR-0061 in the commit before this. Both services are kept in every conflicted file: the Caddy blocks and aliases, the backup and restore tables, the README, and the notes in ADR-0022 and security.md. Actual's restore check is now step 9. Counts recounted from the files: - 19 services, 18 secrets - 12 names, 11 of them host overrides - 17 volumes, 14 archived - 61 ADRs The sensitive backup inventory passes, and so does make validate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r extra linkding (#144, ADR-0061) landed as the fourth tier extra. Actual is now the fifth and the twelfth service in the compose header, and its record was renumbered to ADR-0062 in the commit before this. Both services are kept in every conflicted file, including the CI boot jobs, the Caddy blocks and aliases, the secrets template, the backup and restore tables, the README, and the notes in ADR-0022 and security.md. Counts recounted from the files: - 20 services, 19 secrets - 13 names, 12 of them host overrides - 18 volumes, 15 archived, 9 not rebuildable - 62 ADRs make validate passes. The sensitive backup inventory does not: on main, linkding-data and vaultwarden-data share the sentinel ./db.sqlite3. That came in with #731 and is left for its own fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
This adds Actual, the household's budget, to
stacks/sensitiveon trinity, behind Caddy atactual.matrix.elysium, and records the decision as ADR-0062. The service is authored and validated here; deploying it on trinity comes afterwards and is listed at the end.Actual has no password setting: a new server is claimed by whoever sets the first password. The new
scripts/seed-actual-password.shclaims it from SOPS (ACTUAL_SERVER_PASSWORD), inside the pinned image and with no network, before the service's first start.make upruns it every time, and once the server is claimed it only checks that the SOPS password still logs in.Also in this PR:
actual-datajoins the nightly backup set and the restore uid table.Why
Closes out the decision half of #142. Miniflux (#147, ADR-0057), Memos (#145, ADR-0059), Mealie (#146, ADR-0060) and linkding (#144, ADR-0061) landed first, and HomeBox's decline took ADR-0058, so Actual is the fifth tier extra and its record is ADR-0062. The issue sits under Tier extras, and the roadmap requires each of those to have its own decision before any of it is written. ADR-0062 is that decision:
Blast radius
This affects trinity (VLAN 99) only, and adds one container, one volume and one name. Nothing is deployed by merging.
expose: 5006only.secrets/*.sops.yaml.ACTUAL_SERVER_PASSWORDis read from SOPS by the seed script; it is never in the container's environment or in.env.Known limit (ADR-0062): in password mode every device shares one login session, and changing the password does not sign anyone out. The stack README has a tested command that does.
Verification
Measured on
actualbudget/actual-server:26.9.0-alpine, pinned by digest, on trinity:docker diffis empty./healthwith--network none.account.sqliteis in rollback-journal mode.The seed script was tested on throwaway volumes in four cases, and each gave the expected result:
--checkexits 1Session revocation was tested: after deleting
sessions, the old token gets 401.Actual also booted from the real
compose.yamlon an internal network with a spare subnet (the live stack holds the fixed one): healthy, read-only,CapDrop=[ALL], user 1001:1001. CI's hardened-boot job runs it on the real subnet.make validatepasses. shellcheck was run separately in Docker, because it isn't installed on trinity.Deployed to the lab and confirmed working. Not yet; the steps are:
ACTUAL_SERVER_PASSWORDin SOPS on trinity, and a copy in the password manager.actualhost override on morpheus.make up STACK=sensitive.No real budget goes in before Build the sensitive-tier host on the ProDesk (ADR-0034) #404 step 10.
Docs updated (
docs/, service README, ordocs/roadmap.md)Refs #142
🤖 Generated with Claude Code