Decision sheet D1-D10: hub CHANGELOG, architecture notes (01, 04, 05, 07, 08, 10), register (VERIFY states, R-912), STATUS, report; decoy suite runs in a git worktree
gates / gates (push) Successful in 4m32s
gates / gates (push) Successful in 4m32s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -71,6 +71,8 @@
|
||||
|
||||
| Symbol | File | Short signature | Use for | Gotchas |
|
||||
|---|---|---|---|---|
|
||||
| `store.WindowCreditsBetween` | hub/internal/store/offsite_keys.go | `(customerID, from, to) ([]WindowCredit, unknown)` | R-435: what the hub's clean-up windows may explain | Each window ≤ its `max_remove`; the CALLER spends each window once (`OffsiteChecker.usedWindows`); unknown → the half-rule, never „explained" |
|
||||
| `cloudflare.TokenZones` / `ZoneEquals` | hub/internal/cloudflare/reach.go | `(ctx, base, token) (names, total, err)` | R-138: what zones a pasted token can see | Every failure wraps `ErrReachUnknown` (refuse the save); the token never appears in an error; `/zones` is READ scope, not write |
|
||||
| `offsitekeys.Registrar` (`Install` / `Confirm` / `Audit` / `OpenWindow` / `CloseWindow` / `MoveAside`) | hub/internal/offsitekeys/offsitekeys.go | `(ctx, Target, password, …)` | EVERY write to a sub-account's `.ssh/authorized_keys` and every repo move-aside | **The only writer of that file, and the only deleter on a sub-account (`DeleteSetAside`: `<repo>.orphaned-*` only, decision 74).** `read()` is read-only (R-827). Uses the provider's port-23 restricted shell (`dd of=` takes stdin, `mv` overwrites, `test` does NOT exist — measured); an unpinned line is a deletion route and is dropped on every install; the window line goes FIRST (first match wins). Never `rm`. |
|
||||
| `offsitekeys.Service` (`RegisterKey`, `ConfirmKey`, `AuditAll`, `OpenWindowFor`, `CloseWindowFor`, `SweepExpiredWindows`) | hub/internal/offsitekeys/service.go | — | Binding the registrar to the store, descriptor and operator events | The box-facing API (`/api/v1/offsite/register-key…`) answers with NO credential — pinned by `TestOffsiteKeyEndpoints_AuthAndNoPasswordInAnyResponse`. |
|
||||
| `(*Store).SaveOneTimeSecret` / `OffsitePassword` / `SealLegacyOffsiteSecrets` | hub/internal/store/offsite_seal.go | — | Storing / reading the sub-account password | **Sealed AES-256-GCM; no key → refused (fail-closed).** Under `go test` every store gets a fixed key (`testing.Testing()`); production needs `OFFSITE_SECRET_KEY`. Never serve the value to a box. |
|
||||
@@ -80,6 +82,8 @@
|
||||
|
||||
| Symbol | File | Short signature | Use for | Gotchas |
|
||||
|---|---|---|---|---|
|
||||
| `store.CreateOperatorAction` / `PendingOperatorActions` / `RecordOperatorActionResult` / `ValidateOperatorAction` | hub/internal/store/opactions.go | rows of `operator_actions` | Operator actions carried in the report reply (decision 185); POST `/hosts/{id}/operator-action` → `handleOperatorAction` | `ValidateOperatorAction` IS the closed list (`TestOperatorActions_ClosedList`); a result is matched on (customer, id) — never on id alone |
|
||||
| `intent.Hub.MarkWaitStart` / `MarkWaitEnd` / `Presence` | hub/internal/intent/presence.go | `(customerID, now)` | Box presence from the wait channel (R-30); the delete-host fast path | In memory: a hub younger than 333 s answers `unknown`, which never permits the fast delete; `hostOffDeleteAfter` ≥ the window (pinned) |
|
||||
| `(*Server).hostDetailData` | hub/internal/web/hosts.go (~L282) | `(host *store.Host, r) map[string]interface{}` | The ONE view-model builder for the shared `host_detail_body` sub-template (standalone `/hosts/{id}` + customer Host tab) | Booleans/counts only for DR/escrow; carries `Deletable` (= status != "ok") which gates the danger-zone card. Never add a secret field. |
|
||||
| `parseHostAddresses` + `(*Server).hostNetwork` / `hostNetworkView` (v0.85.0) | hub/internal/web/hosts.go | `(reportJSON) []hostAddressView` · `(host, reportJSON) hostNetworkView` | The host page's Network card: every routable address the box holds + its WireGuard allocation | Needs agent **>= 0.119.0** (`minAgentForAddresses`); below it the wire has no `addresses` key and the card renders **UNKNOWN, never "no addresses"** — an absent signal is not a negative result. WireGuard is TWO facts: the hub's allocation (`GetWGPeerForHost`, authoritative) AND whether the box confirms holding it — the allocation alone cannot distinguish a live tunnel from a peer that was never applied. The WG row is split out by comparing against the ALLOCATION, never by matching the interface name `wg-felhom`, which is a unit name that can change. |
|
||||
| `(*Store).GetHostRecoveryMeta` + `(*Server).handleHostRevealRecoveryCredential` | hub/internal/store/host_recovery.go · hub/internal/web/hosts.go | `(hostID) (*HostRecoveryMeta, error)` · `POST /hosts/{id}/reveal-recovery-credential` | The break-glass console credential, split into a RENDER half and a RETRIEVE half (v0.84.0) | **Use `GetHostRecoveryMeta` on any page-render path** — its struct and its `SELECT` both omit the `secret` column, so it cannot leak one; `GetHostRecoveryCredential` (which does select it) belongs only to the two retrieval handlers. The reveal is POST so the ServeHTTP-level CSRF check applies and no secret is reachable by URL; it writes ONE `recovery_credential_revealed` event via `SaveEvent` and calls NO dispatcher (the `handleRequestLogTail` shape). `api/handler.go handleAdminGetRecoveryCredential` (global key) is the independent fallback for when the UI is down — never route the UI through it. Secret at rest is plaintext → R-133. |
|
||||
|
||||
Reference in New Issue
Block a user