CC secrets build: register (177 -> 181: R-1130..R-1134 opened, R-1122 closed, R-1118 narrowed), STATUS D13-D16, report
gates / gates (push) Successful in 6m52s
gates / gates (push) Successful in 6m52s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# REPORT — BUILD: `sec`, the CC secret broker, and its guards (2026-10-17)
|
||||
|
||||
## For the operator
|
||||
|
||||
- **What works now.** CC's secrets are in one encrypted file in a private Gitea repo. CC uses a secret only by its
|
||||
name, through `sec`. Every CC session on DooPlex has four guards: two stop the known bad commands, two hide any
|
||||
stored secret in what a command prints. A check runs every night at 03:40, searches old session records, and mails
|
||||
you the NAMES it finds.
|
||||
- **What was proven.** A fake secret was in a session record 3 times without the guards, and 0 times with them, in
|
||||
normal sessions and in sessions started with prompts off. A real fresh session refused a bad command and hid the
|
||||
store's self-test value.
|
||||
- **What broke during the run, and is fixed.** For about 4 minutes (13:19–13:23) every CC session saw
|
||||
`/home/«DOCKER_USERNAME»/…` instead of the real home path: a stored user name equals the login. User names are now on
|
||||
a „not secret" list.
|
||||
- **What the first history check found.** Three secrets are in old session records: the registry password, and the
|
||||
two demo recovery codes. The two codes were printed **today at 10:27** by the JARR session (it loaded the old file
|
||||
with `.`); the guard refuses that command now.
|
||||
- **What you decide** (status page D13–D16): remove the Resend key from your `.bashrc`; add a homelab `ask` rule;
|
||||
change the three found secrets; name or drop line 20 of the old file (a value with no name).
|
||||
- **Not changed.** The old credentials file and `~/rotated-secrets-2026-10-09.txt` are untouched. No box, customer,
|
||||
hub or cluster object was changed.
|
||||
|
||||
## Baselines
|
||||
|
||||
| Repo | at start | at end |
|
||||
|---|---|---|
|
||||
| felhom.eu | `498b9139` (the brief named `0fc4142b`; one later commit, R-1129's CI workflow — nothing in this area) | see `git log` |
|
||||
| homelab-manifests | `origin/main` `2e00111` (brief: `e8e595a`; later commits are renovate/jarrs only, none in `.claude/`) | not touched |
|
||||
| cc-secrets | empty (made by the operator) | `08c986c` and later |
|
||||
|
||||
Claude Code **2.1.300** (unchanged since the spike). Architecture: none existed; it is now
|
||||
`cc-secrets/ARCHITECTURE.md`, with the design verbatim at `cc-secrets/docs/DESIGN-cc-secrets-2026-10-17.md` and its
|
||||
differences in §0.
|
||||
|
||||
## What was built (`admin/cc-secrets`)
|
||||
|
||||
`sec` (`ccsec/cli.py`), the mask (`ccsec/mask.py`), the store (`ccsec/store.py`), the hooks (`ccsec/hook.py`,
|
||||
`ccsec/rules.py`), the sweep + mail (`ccsec/sweep.py`, `ccsec/mail.py`), `sec doctor`, the installer (`install.py`),
|
||||
systemd user units, 55 tests and `tests/redproof.py`. Pins: sops 3.10.2, age 1.2.1 (`pins.json`).
|
||||
|
||||
## Evidence (`documentation/audits/cc-creds-build-2026-10-17/`)
|
||||
|
||||
| what | result | file |
|
||||
|---|---|---|
|
||||
| repo private | anonymous page/raw/API 404, `info/refs` 401; same raw file authed 200; public felhom.eu file 200 | `privacy-before-first-push.txt`, `privacy-after-first-push.txt` |
|
||||
| hooks with prompts off | deny held; transcript `permissionMode: bypassPermissions` | `mode-test.txt` |
|
||||
| Read / Grep shapes | Read `{type, file:{content,…}}` replaced (control 3 copies → 0); **Grep is not a tool in 2.1.300** (it answered „No such tool available"; searches go through Bash) | `read-grep-results.txt`, `shape.log` |
|
||||
| unit tests | 55 OK | — |
|
||||
| red-proofs | 18 mutations, each failed its test; suite OK after restore | `redproof.txt` |
|
||||
| child sessions | 36/36 in both modes (9 refusals + Read refusal, 2 controls ran, Bash/Read masked, large output scrubbed, crash withheld, 2 no-hook controls visible) | `proof-table.txt` (+ reruns) |
|
||||
| sweep of child records | only the 4 no-hook controls hold a fake value | `proof-sweep.txt` |
|
||||
| install | 7/7 fixtures, backup `settings.json.bak-ccsec-20261017-131420` | `install-real.txt` |
|
||||
| fresh real session | refusal; self-test value masked, 0 in record and answer, its name in the note | `fresh-session-proof.txt` |
|
||||
| import | 23 names + lengths; old file sha unchanged | `import.txt`, `credentials-sha-before.txt` |
|
||||
| history sweep | 1 494 files, 1.13 GB, 175 s; names below; mail accepted | `history-sweep.txt`, `history-sweep-list.txt` |
|
||||
|
||||
`updatedToolOutput` shapes used: Bash `{stdout, stderr, interrupted, isImage, noOutputExpected}` (copied, strings
|
||||
masked); Read `{type, file:{filePath, content, numLines, startLine, totalLines}}`. With it, `additionalContext` names
|
||||
the masked keys (proven in child records: the note appears 3 times, the value 0).
|
||||
|
||||
## History sweep — names found (no values)
|
||||
|
||||
`R_DEMO_FELHOM`, `R_DEMO_HP` (1 file, JARR session, 2026-10-17 08:27 UTC, `. ~/.config/credentials`), `DOCKER_PASSWORD`
|
||||
(1 file, a helper-agent record of 2026-09-21). User names also matched before the plain-names fix (not secrets).
|
||||
Records older than ~2026-09-16 are no longer on disk, so the July/August leaks (R-132 etc.) cannot be found by it.
|
||||
|
||||
## Things that went wrong, and what caught them
|
||||
|
||||
1. **Masked home path in every session, ~4 min** (above). Caught by looking at the sweep's names: `DOCKER_USERNAME` in
|
||||
414 files. Fixed: `store/plain-names.txt`, counted only for `*_USER`/`*_USERNAME` (after a security review said the
|
||||
list must not be an off switch). Tests + 2 red-proofs.
|
||||
2. **Tests and commit in one command** (workspace rule 1): `d70b57e` was pushed with 2 failing tests. The code was
|
||||
right; my new check was over-strict („the sec runtime folder" contains „sec run"). Fixed in `f1456ae`.
|
||||
3. **A control that saw nothing:** the `/proc` sampler's control used `sleep 0.3 VALUE` (exits at once on a bad
|
||||
argument), then `bash -c "sleep" x VALUE` (bash execs sleep and drops the argument). The third carrier worked.
|
||||
4. **A red-proof that read green** because Python reused a stale `.pyc` (two equal-size mutations in one second).
|
||||
Red-proofs now run with `-B` and no cache.
|
||||
5. **The child proof's large-output rows** first read „LEAK" with 0 copies anywhere: my check looked for the mask mark
|
||||
only in the transcript, while the scrub happens in CC's side file. Fixed the check, re-ran.
|
||||
6. **A child session was refused by the model's safety filter** (its prompt about a Grep search); child sessions run on
|
||||
Sonnet. The hooks do not depend on the model.
|
||||
7. **The import refused the whole file** (line 20 has no name). Named lines were imported from a filtered RAM copy
|
||||
(0600, shredded); line 20 waits for the operator (R-1134).
|
||||
|
||||
## Register
|
||||
|
||||
Rows before **177**, after **181**. Opened **5**: R-1130, R-1131, R-1132 (found secrets), R-1133 (`~/.git-credentials`),
|
||||
R-1134 (retire the old file; line 20; FACEBOOK_API and Tuya keys are in neither file). Closed **1**: R-1122. Narrowed:
|
||||
R-1118. Still open from the spike: R-1119, R-1120 (STOP 3), R-1121.
|
||||
|
||||
## Recommendations not followed (workspace rule 4)
|
||||
|
||||
- **Values under 8 characters are stored, not refused** (design §3.1 says refuse): the build brief asks to store them.
|
||||
- **`~/rotated-secrets-2026-10-09.txt` not moved into the store** (design §6.3): the build brief forbids touching it.
|
||||
- **`--uninstall` removes our entries instead of restoring the backup** (brief §5.5): a restore would also undo every
|
||||
unrelated settings change; `--restore <backup>` exists for the byte-exact case.
|
||||
- **The sweep mail does not reuse `felhom-backup-failmail`** (brief §4.6): its key file is root-only. It reuses the
|
||||
same Resend request shape with `RESEND_API` from the store.
|
||||
- **The import went through a filtered copy** instead of stopping: the refusal was correct and specific (one nameless
|
||||
line), and the named keys could move now; the decision on line 20 stays with the operator.
|
||||
- **`docs/DESIGN…` + `ARCHITECTURE.md` rather than the design AS the architecture doc:** the built system differs in
|
||||
six places, listed in §0.
|
||||
|
||||
## Fences kept
|
||||
|
||||
`~/.config/credentials` unchanged (sha prefix before = after). `~/rotated-secrets-2026-10-09.txt` not opened. No `sops
|
||||
set`, no `git add -A`, no `git add -f`. Real values never printed: every check here prints names, lengths or
|
||||
8-character hash prefixes. Lingering is NOT enabled (`Linger=no`): a user timer runs only while kisfenyo's user manager runs. Whether it
|
||||
fires at 03:40 tonight is **not proven** — tomorrow `sec doctor` shows the sweep's age (it alarms after 36 h). Enabling
|
||||
linger (`sudo loginctl enable-linger kisfenyo`) would remove the doubt; not done, a system change left to the operator.
|
||||
`rclone/rclone:latest` was pulled once for the read-only R2 proof.
|
||||
|
||||
## Teardown
|
||||
|
||||
- **DooPlex:** `~/cc-creds-build/` (0700) kept: test store with fake values, harness, canaries. `/run/user/1000/cc-secrets/fake/X`
|
||||
(a fake value) is in RAM and goes with the next reboot. Child-session records with fake values stay under
|
||||
`~/.claude/projects/-home-kisfenyo-cc-creds-build-work/`.
|
||||
- **Other hosts:** none. **Hub:** not touched. **Cluster:** not touched (the Resend recipe was tested with
|
||||
`--dry-run=client` only).
|
||||
@@ -40,6 +40,7 @@
|
||||
- Tester 2 has never made an off-site check (R-862). The scratch box cannot read the drill catalog (R-992).
|
||||
|
||||
## What we work on
|
||||
- **CC's secrets** (R-1118): built and installed 2026-10-17 — the store, `sec`, the guards in every CC session, the nightly check. Left: the old credentials file retires after a week with no reader (R-1134), and three answers from you (below).
|
||||
- **After 2026-10-18 14:16 UTC** (demo-hp's next clean-up window): check that the old restic (image 0.322.0) still lists and restores demo-hp's store after its first real 0.19.1 prune (`audits/night-restore-2026-10-17/tools/rx.sh`). Everything else of R-1105/R-1107 is done.
|
||||
- The power-cut findings still open (R-1004, R-1005, R-1010, R-1012, R-1013).
|
||||
- Media apps on every drive (R-1023): live with the 0.313.0 floor — every drive in every media app, a new drive made ready by the box, a pulled drive loses nothing, a safe removal lets go first and the drive comes back by itself.
|
||||
@@ -59,8 +60,11 @@
|
||||
| D10 | R-1125 | Raise the 23 memory requests to what the apps really use? Then an 8 GB box no longer installs the full household set (its sixth app is refused). | **Your call** — values ready | Keep: the box's install check stays optimistic | nothing changes | — |
|
||||
| D11 | R-1126 | The tunnel runs before the claim (the claim needs it). Close FileBrowser's first-start window (A), or claim only at home (B)? | **A** — small, no promise changes | B: the claim mail and flow change, the household must be at home | FileBrowser's default is open for ~2 s at its first start | — |
|
||||
| D12 | R-1129 | CI has never sent its failure mail (the key is empty in the runner since at least 2026-09-22). Set the key in the four repos? | **Yes** — you set it, or CC with your word (it is a credential) | — | a red CI run or a `--no-verify` push reaches nobody | — |
|
||||
| D13 | R-1119 | Remove `export RESEND_API=…` from your `~/.bashrc` (line 88)? The key is in CC's store now (`sec run RESEND_API`). | **Yes** — you, or CC with your word (it is your file) | Keep: every CC command carries the key | the key stays one `env` away from a record; the new guard refuses bare `env`/`printenv`, but not every shape | — |
|
||||
| D14 | R-1120 | homelab CC settings let `kubectl get secret -o yaml` run with no prompt. Add an `ask` rule too, or rely on the new guard alone? | **Add the ask rule too** — one line, two layers | Guard only: one layer, already proven | the guard alone refuses it in every session | — |
|
||||
| D15 | R-1130, R-1131, R-1132 | Three secrets are in old CC records: the registry password, and the demo-felhom and demo-hp recovery codes (printed today 10:27 by the JARR session, before the guards). Change them? | **Change the registry password; your call on the two demo codes** | Accept: no work; the values stay valid | the three values stay valid while copies sit in records Anthropic's API has seen | — |
|
||||
| D16 | R-1134 | Line 20 of the old credentials file is a value with NO name. What is it? | Tell CC the name (CC stores it) or say „drop it” | — | it stays only in the old file, which retires on 2026-10-24 | the old file's retirement |
|
||||
| D9 | R-1062 | The N100's USB drive is also its whole-box backup drive. Split it in two anyway? | **No — keep one drive**; the lived-in box keeps its nightly whole-box backup | Split: the box loses its local whole-box backup (or add a second drive first) | it stays one drive; multi-drive tests stay on Tester 1 | — |
|
||||
| D10 | R-1118 | Where should CC keep its secrets, so it can use one without ever printing it? (spike 2026-10-17: the guards work; `audits/SPIKE-cc-creds-2026-10-17.md`) | **B — an encrypted file in a private Gitea repo (SOPS + age)**; then you make one empty private repo, CC cannot (its tokens lack the right) | Vaultwarden: one more service to run and back up; the spike's store test would be redone | the plain credentials file stays, and every known leak shape (sourcing it, `printenv`, curl URL echo, `kubectl get secret`) stays open | the build of the broker and guards |
|
||||
|
||||
## Waiting on you — do
|
||||
- **Nézd meg a demo-hp-n a Calibre-Web „Adatok és mentés" fülét és a Fájlkezelőt** (`felhom.enkisfelhom.hu`, controller 0.321.0, minden dobozon kiadva). A mentés egy részben, egyszerű szavakkal; a Fájlkezelő nem kér frissítést és nem ajánl „Install app"-ot.
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-17 — the CC secret broker `sec` and its guards (`admin/cc-secrets`)
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|---|---|---|---|
|
||||
| **R-1122** | **`skills/felhom-build-deploy/SKILL.md` told CC to `source ~/.config/credentials` for the ISO publish.** (P3) | CLOSED 2026-10-17 — the ISO publish and the hub-token recipe go through `sec run`; the values reach docker as `-e NAME` and curl through a header file, never on a command line. Both recipes proven: a local server saw the token's hash and length; a read-only `lsf R2:felhom-iso` listed the bucket. | `ba40c0a2`; `audits/cc-creds-build-2026-10-17/` |
|
||||
|
||||
## 2026-10-17 — Tester 1 on the live catalog, decoy suites in every runner, memory limits, FileBrowser (controller v0.326.0, catalog `bff82f9`)
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|
||||
@@ -212,7 +212,7 @@ stopping line that lies.
|
||||
| **R-1094** | Storage & devices | P4 | **The whole-disk format refusal tells the household to format the free part — which is refused while the other part is in use.** SEEN 2026-10-16 (controller 0.318.0): 409 „Ezen a lemezen egy használatban lévő meghajtó van, ezért a teljes lemez nem formázható. Csak a szabad részét formázhatod." — since agent 0.158.1 a part next to a mounted part can only be USED (the root wrapper trusts no „Felhom's own" claim). Evidence: `audits/storage-identity-buildB-2026-10-15/live/14-samedisk-refusal-offer.txt`. | READY | — | Reword `err.web.meghajto_hasznalatban_formazas` (hu + en): „… nem formázható. A szabad részét használatba veheted." Next controller release; the copy gates run. | CC |
|
||||
| **R-1124** | Storage & devices | P4 | **A drive decommissioned and then unplugged keeps its agent mount unit enabled on the host** (`mnt-<name>.mount`, „Managed by felhom-agent”, `WantedBy=multi-user.target`), so every later boot waits for a device that is gone, and the agent's watchdog reports it „disconnected”. Decommission retires the drive logically (agent: „logical retire, data untouched”) and releases only the guest bind; no product path releases the host mount or forgets the drive. Seen on Tester 1 at the R-1022 teardown 2026-10-17 (four units left; the boot delay is read from the unit, NOT measured). | OPEN — filed 2026-10-17 | — | Decide „forget a decommissioned drive” (unit disabled + removed when the drive is absent), with a test | CC |
|
||||
|
||||
## Security & access — 22 rows (P1 1, P2 2, P3 13, P4 6)
|
||||
## Security & access — 27 rows (P1 1, P2 2, P3 18, P4 6)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -233,10 +233,15 @@ stopping line that lies.
|
||||
| **R-1040** | Security & access | P4 | **Until the household makes Sonarr's or Radarr's first admin, any container on the box can read the app's API key.** Measured on the bench 2026-10-14 with the R-1030 seed in place: `AuthenticationRequired=Enabled`, `AuthenticationMethod=None` — `/initialize.json` from another container answers 200 with `apiKey`. Outsiders are blocked by the box's setup gate in that window; a compromised other app is not. | **OPEN** — filed 2026-10-14 | — | Seed the API key at install (`SONARR__AUTH__APIKEY`, measured to work) so the box knows it, and close the window by telling the household in the first steps; or seed a Forms user | CC |
|
||||
| **R-1050** | Security & access | P4 | **Raise HSTS to one year on or after 2026-11-15, if no https trouble was reported.** `09` §3 decision 249 (D2 = A, operator 2026-10-15): HSTS stays `max-age=86400` now; after a month with no https trouble it becomes one year (`31536000`); never preload, never `includeSubDomains`. The value is `HSTSMaxAgeSeconds` in `felhom-controller/controller/internal/infra/infra.go`, pinned by `TestRenderForwardedHeaders_HTTPSOnly`. „Trouble” = any report of a name that will not open, or a box answering http after the floor. | **WATCHING** — filed 2026-10-15 | the date | On or after 2026-11-15: read the register and the operator's mail for https trouble since 2026-10-15; none → a controller release with one year (test updated), bake → vouch → floor | CC |
|
||||
| **R-1054** | Security & access | P3 | **The hub's operator/global API key is still one bearer that can do everything on `/api/v1`, including reading every box's break-glass root password (`GET /admin/hosts/{id}/recovery-credential`).** Hub v0.152.0 gave the web surface scoped tokens (`05` §17) but tokens are refused on `/api/v1` by design. Who uses the global key today is not listed in one place. Fix: list its consumers; move each operator use to a scoped token on the web side (or a token class on the API), and keep the global key only where a box path needs it — or retire it. | OPEN — filed 2026-10-15 by the token build | — | List the global key's consumers (pod env, scripts, runbooks) | CC |
|
||||
| **R-1118** | Security & access | P3 | **No architecture document covers how CC obtains and uses secrets; four transcript leaks and one wiped credentials file came from that gap** (R-132, R-453, R-1088, `audits/CAMPAIGN-11-recovery-journey-2026-08-05.md` §7). `12-agent-tooling.md` covers instruction files only, `runbooks/secrets.md` cluster Secrets only. The proposed design (encrypted store + `sec` broker + CC guards + nightly sweep) was spiked 2026-10-17 with fake secrets (`audits/SPIKE-cc-creds-2026-10-17.md`): output mask PASS, PreToolUse refuse PASS, PostToolUse replace PARTIAL (a crashing hook and a large output each put the value on disk; both have a measured fix), SOPS + age store PARTIAL (works locally; the private repo could not be created: no token with `write:user`, push-to-create off), transcript sweep PASS. The design file `DESIGN-cc-secrets-2026-10-17.md` was not on DooPlex, so it is not in git yet. | WAITING-ON-OPERATOR | the operator's store choice (B, SOPS + age in a private Gitea repo, recommended; or Vaultwarden) and the design file | Operator: pick the store; put the design file at `audits/SPIKE-cc-creds-2026-10-17/DESIGN.md`; for B, create the empty private repo `admin/cc-secrets-spike` (or give CC a token with `write:user`). Then a build TASK: the architecture document first, then broker and guards with the spike's two fixes | operator → CC |
|
||||
| **R-1118** | Security & access | P3 | **CC's secrets moved off the plain `~/.config/credentials` into an encrypted store with a broker and guards; what is left is retiring the old file.** BUILT 2026-10-17 (store B, operator ruling 11:28): private repo `admin/cc-secrets` (`ARCHITECTURE.md`, the design verbatim in `docs/`), `sec` (no get), PreToolUse refusals + PostToolUse masking for Bash and Read in `~/.claude/settings.json` (every CC session on DooPlex), nightly transcript sweep with mail. 55 tests, 18 red-proofs, 36-row child-session proof in both launch modes, fresh-session proof (`audits/cc-creds-build-2026-10-17/`). 25 names in the store (23 imported, `RESEND_API` from CC's environment, a self-test value). `12-agent-tooling.md` §5 points at it. | NARROWED — built; the old file's retirement is R-1134 | the week with no reader (R-1134) | `sec doctor` stays all OK; R-1134 | CC |
|
||||
| **R-1119** | Security & access | P3 | **`RESEND_API` is exported into CC's own environment from `~/.bashrc` (line 88), so every Bash call CC makes carries it, and a plain `printenv`, `env` or `set` prints it into the transcript.** MEASURED 2026-10-17 (SPIKE Q6, names only): present in the parent `claude` process environment and in the Bash tool environment. | OPEN | R-1118's broker, for the clean fix (the value lives in the store and reaches only the command that names it) | Until then: find what reads it (memory: the Resend checks run it via `bash -ic`), then move the export out of `~/.bashrc` into that one script — the operator's shell file, so the operator's word | operator |
|
||||
| **R-1120** | Security & access | P3 | **`homelab-manifests/.claude/settings.json` allows `Bash(kubectl get:*)` without a prompt, and that pattern includes `kubectl get secret -o yaml`, which prints every value (base64) into the transcript.** Read 2026-10-17 at `origin/main` `e8e595a`, line 6; nothing in the file asks for Secret reads to skip the prompt. The local clone `~/git/homelab-manifests` is behind origin and has uncommitted edits to that same file (not touched). | OPEN | — | Add `Bash(kubectl get secret:*)` and `Bash(kubectl get secrets:*)` to `ask` (ask wins over allow), or a PreToolUse refusal like the spike's `kubectl-read-secret` rule. A permission change, so the operator's word (`.claude/rules/unprompted-work.md` §5) | operator |
|
||||
| **R-1121** | Security & access | P3 | **`~/rotated-secrets-2026-10-09.txt` still exists on DooPlex** — 632 bytes, modified 2026-10-09 13:08 +0200, mode 600 (SPIKE Q6: `test -e` + `stat` only, not opened). Left by the R-925 rotation work. If any value in it is still valid, it is one more plaintext secrets file in the home folder, readable by every CC session. | WAITING-ON-OPERATOR | what the file holds — only the operator knows | Operator: confirm every value in it is revoked, then `shred -u` it; or move the live ones into the R-1118 store | operator |
|
||||
| **R-1130** | Security & access | P3 | **`DOCKER_PASSWORD` (the Gitea registry login CC uses) is in one old CC session record** — a helper-agent transcript of 2026-09-21 (`~/.claude/projects/-mnt-5-hdd-felhom-eu-git/5f83c0fb…/subagents/agent-a8614eb6ee2a17e6c.jsonl`). Found by the first history sweep 2026-10-17 (`audits/cc-creds-build-2026-10-17/history-sweep-list.txt`; names only). The record has reached Anthropic's API; the value is still valid unless changed. | WAITING-ON-OPERATOR | the operator rotates it, or tells CC to (file → store with `sec put`) | Rotate the registry password; `sec put DOCKER_PASSWORD --from-file F`; check an image pull | operator |
|
||||
| **R-1131** | Security & access | P3 | **demo-felhom's recovery code (`R_DEMO_FELHOM`) was printed into the JARR session's record today, 2026-10-17 08:27 UTC**, by `set -a; . ~/.config/credentials` — the Campaign 11 §7 shape (a hyphenated key bash cannot assign, so bash prints the line). This was 3 h before the guards were installed; that command is refused now (`source-secret-file`). Record: `~/.claude/projects/-home-kisfenyo-git-jarr/c9d98cda….jsonl` line 2855 (a tool result). | WAITING-ON-OPERATOR | the operator: re-issue the code, or accept (a demo box) | Decide: re-issue demo-felhom's recovery code, or accept and close | operator |
|
||||
| **R-1132** | Security & access | P3 | **demo-hp's recovery code (`R_DEMO_HP`) was printed in the same JARR record line as R-1131** (2026-10-17 08:27 UTC, the same `. ~/.config/credentials`). | WAITING-ON-OPERATOR | the operator: re-issue, or accept (a demo box) | Decide together with R-1131 | operator |
|
||||
| **R-1133** | Security & access | P3 | **`~/.git-credentials` on DooPlex holds Gitea tokens in clear text** — three entries (`kisfenyo`, `Kisfenyo`, `admin`); the `admin` token has `write:repository` (and misc, package, issue, notification). Any `cat` of it prints them; the new PreToolUse guard refuses reads of that path (`read-secret-file`), but git itself must keep reading it. Out of the 2026-10-17 build's scope by its brief. | OPEN | — | Decide: move git to a credential helper that reads from `sec`, or accept the file (0600) with the guard | operator → CC |
|
||||
| **R-1134** | Security & access | P3 | **Retire the old `~/.config/credentials`**: every named key is in the CC store since 2026-10-17 13:16 (23 imported; the two hyphenated names became `R_DEMO_FELHOM`, `R_DEMO_HP`). **Two open points:** (1) its line 20 is a 32-character value with NO name — the import refused it (one bad line refuses all) and CC imported the named lines from a filtered copy; only the operator knows what it is. (2) `FACEBOOK_API` and the Tuya keys, which `fb_probe.py` and the bench README name, are in neither the file nor the store. | WAITING-ON-OPERATOR | a week with no reader (from 2026-10-17), and the operator's go | 2026-10-24: check no reader (sweep + `hook.log`); operator names line 20 (or drops it); then rename the file to `.retired`, and delete only on the operator's go | operator → CC |
|
||||
| **R-1126** | Security & access | P3 | **The Cloudflare tunnel runs before the box is claimed, and no document says whether that is intended.** The claim needs it: the claim mail links `https://felhom.<domain>` (`hub/internal/notify/templates.go:27`); `cloudflared` is started from the token with no claim check (`controller` `stacks/infra.go:85-90`; `01-topology-and-trust.md:119`, `:248`). Reachable before the claim: the dashboard serves only the claim page (code: 72 h life, 5 tries, 15 min lockout — `web/claim.go:22-33`), and `files.<domain>` — FileBrowser on its default login from its first start until the next base-stack tick sets a password (seconds; v0.326.0 re-checks every run). Options: (A) keep the tunnel; route FileBrowser publicly only after its default is replaced — small, no promise changes; (B) start the tunnel only after a LAN claim — the claim flow and its mail change, the household must be at home. Recommended A. From R-977. | WAITING-ON-OPERATOR — filed 2026-10-17 | the operator's choice A or B | Write the choice into `01` §5; build A (or B) | CC |
|
||||
|
||||
## Box system & updates — 12 rows (P2 1, P3 7, P4 4)
|
||||
@@ -311,7 +316,7 @@ stopping line that lies.
|
||||
| **R-990** | Business & legal | P4 | **Operator: read `documentation/audits/forum-research-2026-10-12/FELHOM.md` §5 and rule on each recommendation.** READ-ONLY research 2026-10-12: three years (#7943–#11092, 2,908 posts, 195 people, 82 beginner threads) of the public PROHARDVER! topic „Home server / házi szerver építése”, tagged and counted by person (`FINDINGS.md`, method and spot-check in `METHOD.md`; no username or post text in git). Ten ranked recommendations, none acted on; the top three: a „which machine works” page (8 of 33 beginners who name their hardware own only a Synology, a router disk or a Raspberry Pi — the installer cannot run there; the same gap is R-989's first recommendation from a different source, and the shipped-page half is R-991), „you do not choose an operating system — we do” (the biggest beginner question, 20 of 82 threads), and „bring your old office PC” as the cheap entry. Catalog gap (≥ 3 people): a DLNA server (7), an ad/DNS blocker (5), a camera recorder (5). | **WAITING-ON-OPERATOR** — filed 2026-10-12 | the operator's reading | Operator: rule on each of the ten (yes / no / later). If nothing is done: nothing changes; the research ages | operator |
|
||||
| **R-1035** | Business & legal | P4 | **Did the „felhő tárhely” page earn anything? Two dated checks.** **Baseline (reported by the operator from Search Console, 2026-10-14, last 3 months):** 1 click, 48 impressions, CTR 2.1 %, average position **73.6**; „felhő tárhely” 19 impressions, „felhőtárhely” 5, „felhő alapú tárhely” 3. CC measured separately that the phrase „felhő tárhely” appeared **0 times** on the home, FAQ and backup pages — Google was showing the home page for those queries by accident. `/sajat-felho-tarhely` + `/en/own-cloud-storage` published **2026-10-14**; full baseline, price sources and the power provenance in `audits/seo-2026-10-14/BASELINE.md`. **What to measure, both times:** impressions and average position for „felhő tárhely” and „saját felhő tárhely”, plus the new page's own impressions, clicks and position. **A null result at +4 weeks means nothing on its own** — a new page on a site at position 73.6 takes weeks to months to move, and „felhő tárhely” alone is held by Microsoft, Google and the big list sites; the page aims at the two longer phrases. That is why there are two checks and not one, and why neither should be read as a verdict on the copy. **There is no Search Console connector in this project, so CC cannot read these numbers** — the operator reads them, CC records and compares. **Blocked until the operator asks for indexing** (Search Console → URL-ellenőrzés → the URL → „Indexelés kérése”): until that happens, or Google finds the page itself, both checks read zero for a reason that is not the page. **This is a slow channel and does not replace talking to people.** | **WATCHING** | operator: request indexing, then supply the Search Console read | 2026-11-11: first read (+4 weeks); 2026-12-09: second (+8 weeks), compared with the first | operator + CC |
|
||||
|
||||
## Process & tooling — 32 rows (P2 1, P3 10, P4 21)
|
||||
## Process & tooling — 31 rows (P2 1, P3 9, P4 21)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -327,7 +332,6 @@ stopping line that lies.
|
||||
| **R-902** | Process & tooling | P3 | **The contact form's mailer has no source in any repository: the running image cannot be rebuilt or changed.** FOUND 2026-10-08 (website refresh, brief Part C5). `manifests/contact-mailer.yaml` names `contact-mailer:latest` with `imagePullPolicy: Never` (imported into k3s by hand); the binary in the pod is `/app/contact-mailer` dated 2026-02-05, Go module `github.com/felhom/contact-mailer` (devel). Searched: `git log --all` of felhom.eu (only the manifest), every repo under `/mnt/5_hdd/felhom.eu/git`, `find /mnt/5_hdd/felhom.eu ~ -maxdepth 5 -iname '*contact-mailer*'`. READ from the binary (strings, read-only `kubectl exec … cat`): ONE send per form (`sendViaResend`) to `TO_EMAIL`, `reply_to` = the visitor; nothing is sent back to the visitor; the operator's notification is Hungarian (`buildEmailHTML`, „Csatolmány … fájl"). The English form needs no mailer change (its subject labels carry „(EN)"). `audits/website-refresh-2026-10-08/mailer.md` | **OPEN — NARROWED 2026-10-08 (search done, NOT FOUND): owner: operator.** A read-only search found no source on DooPlex (55,548 `*.go`/`go.mod`/`Dockerfile*` files, every disk, media/Longhorn/containerd/PBS excluded by name), in the web editor's storage and file history, in all 10 Gitea repositories' history, or in Docker's build cache (oldest record 2026-09-28). The binary's own clues: one file `/build/main.go`, go1.23.12, no `vcs.*` stamp; image built 2026-02-05 10:40 by BuildKit. The binary is now KEPT in git (`audits/mailer-source-2026-10-08/contact-mailer.bin`), so a DooPlex rebuild no longer loses the program. A replacement plan is written (`audits/mailer-source-2026-10-08/PLAN-replacement.md`); one question left: is the February folder on the operator's Windows workstation? | — | Operator: look on the workstation; if absent, build the replacement from the plan (attended deploy) | operator |
|
||||
| **R-934** | Process & tooling | P3 | **Pull the failed disk ST3000VX010 S/N Z6A07P2G out of DooPlex; follow the checklist.** The data was moved off at the first alarm (2026-10-10); the disk is unmounted, its fstab line commented out since 2026-08-14, k3s no longer waits for it, and its empty Longhorn disk entry was removed 2026-10-12. Its two SMART alarms (352 unreadable / offline-uncorrectable sectors, flat since 2026-10-09) are silenced by serial until **2026-11-11 14:32 UTC** — if the disk is still in the machine then, they come back on purpose. **A TWIN of the same model (S/N Z6A070XZ, `/mnt/3_hdd`) is in use: read the serial before unplugging.** Checklist: `runbooks/dooplex-remove-disk-Z6A07P2G.md`. | **OPEN** — NEW 2026-10-12 | the operator (hands, a power-off window) | Follow the checklist; after the first boot remove the exporter line and the silence; tell CC | operator |
|
||||
| **R-935** | Process & tooling | P3 | **Something sets `core.bare = true` on the shared `felhom.eu` checkout, and then every git command there fails („this operation must be run in a work tree").** SEEN 2026-10-12 evening twice: `.git/config` changed at 16:29:37 and 16:37:33 CEST, each time with `core.bare=true`; restored by hand both times (`git config core.bare false`, a flag only, no file touched). **Not caused by the gates:** `repo_gates.py --fast` run plainly and with `GIT_DIR=.git` (as a hook gets it) left the flag `false`; a scratch `git init --bare <path>` under `GIT_DIR` does not flip the parent either. The first flip came while this session did not touch the repo; another session pushed to main at 16:44 from elsewhere. | **OPEN** — 2026-10-12 later: across this session's next push the flag stayed `false` (read before and after); not this session's pushes. **OPEN** — NEW 2026-10-12 **Note 2026-10-12 (security review):** at least some of these flips match the security review's pushes from a git worktree of this clone (about 16:29 and 16:37): inside the pre-push hook, `scripts/test_gate_decoys.py`'s poster-facts case runs `git init`/`git config`/`git commit` with the hook's inherited `GIT_DIR`, so it wrote into the real repository — four test commits in the worktree and `user.name d` / `user.email d@d` in the shared `.git/config` (the review removed that `[user]` section at about 16:50; commits 6c2c3dd5 and 186f3aa3 carry that author). The review stopped pushing from the worktree. Fix: the test helper must drop `GIT_DIR`, `GIT_WORK_TREE` and `GIT_INDEX_FILE` from its environment. | finding the writer | Watch `.git/config` (an inotify or audit rule on the file) during a day with the parallel sessions; name the command; fix it there | CC |
|
||||
| **R-1122** | Process & tooling | P3 | **`skills/felhom-build-deploy/SKILL.md` line 153 still tells CC to `source ~/.config/credentials` for the ISO publish** — the shape that printed two recovery codes into a transcript (Campaign 11 §7; memory `credentials-file-sourcing-leaks-codes`), while line 75 of the same skill says never to `source` that file. Found 2026-10-17 by the CC-secrets spike, which stayed docs-only by its brief. | OPEN | — | Replace with three `scripts/read_credential.py` reads into 0600 files (or `sec run` once R-1118 is built) | CC |
|
||||
| **R-206** | Process & tooling | P4 | **The build-cache cap and the weekly prune exist only as a hand-edited `/etc/docker/daemon.json` on DooPlex — not in Ansible, so a rebuild loses them.** The `node_housekeeping` role must also carry the prune, which today it is forbidden to run | **NARROWED** — **2026-10-12 evening: part (a) is in git** (homelab-manifests `eba52d7`, role `docker_daemon`, tags `never` + `docker_daemon`: a byte-copy of the live `daemon.json` and a restart-and-verify handler). **Not installed** — installing restarts the Docker engine on DooPlex (the operator's window). Part (b), narrowing the housekeeping ban to `docker builder prune -af`, loosens a safety rule: the operator's word. Part (c) is in the role's handler. **READY (M) — NEW 2026-08-05** | — | Operator: install in a window (`ansible-playbook site.yml --tags docker_daemon`, check first) — and say yes/no to (b) | operator |
|
||||
| **R-928** | Process & tooling | P4 | **Two sessions running `repo_gates.py` at the same time in the shared `felhom.eu` clone see each other's DECOY plants.** SEEN 2026-10-10 ~15:40: while another session's gate run was in its decoy suite, this session's working tree showed `website/assets/crafty-controller-logo.png` renamed to `crafty-logo.png`, `website/en/apps.html` with a leftover text language switch, then `website/en/contact.html` and `scripts/felhom-host-install.sh` modified — each a plant of `test_gate_decoys.py`, each gone when that run ended. This session's own `site_gates.py` failed three times in that window on plants, not on its change, and a `git add` of a broad path at that moment would have committed a plant. A killed run (`timeout`) leaves its plant in place (its cleanup never runs). Fix direction (a design): decoys plant into a temporary COPY of the tree (as the catalog's volume-persistence decoys already do), or a lock file that a second run waits on. | **OPEN** — filed 2026-10-10 (register-shrink session) | — | Design the copy-or-lock; meanwhile stage explicit paths only (already the rule) and re-run a red site gate once another session's run has ended | CC |
|
||||
| **R-394** | Process & tooling | P4 | **`felhom-build-deploy/SKILL.md` is 179 lines, over the 150-line limit its own repo now enforces.** Found 2026-08-25 by `scripts/check_skills.py` on its first run — the over-length was discovered BY the new checker, on the day the limit was written down, which is the checker working as intended. **It is not edited and not trimmed here:** the task that introduced the limit explicitly scoped the four pre-existing skills out, and trimming a build-and-deploy skill without exercising its commands is how a wrong command ships to a live host. **It is a named single-entry exception in `GRANDFATHERED` in `scripts/check_skills.py`, printed as a WARN on every run**, so it cannot fade; a NEW skill over the limit is convicted normally, and growing the set requires editing that file in a commit with a row to name. **The rationale for the limit** — attention thins across the excess, so the lines that matter are not the ones that survive — is in `skills/felhom-doc-authoring/SKILL.md` §5. | **OPEN — LOW** | — | Trim `felhom-build-deploy/SKILL.md` under 150 lines in a session that can VERIFY the commands it keeps, then delete its `GRANDFATHERED` entry in the same commit. The likely trim is the per-artifact command blocks moving behind a pointer to the runbooks, keeping the gotchas inline — but that is a judgement for a session with a build to run, not a line-count exercise. | CC |
|
||||
|
||||
Reference in New Issue
Block a user