docs: CHANGELOG v0.113.0 + REPORT + CONTEXT + README NAS section + REUSE seams
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,5 +1,41 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v0.113.0 — NAS verify-before-commit + page redesign + protocol-honest guidance (2026-07-11)
|
||||||
|
|
||||||
|
Kills the "bogus share sits at Készenlét forever" bug: `POST /api/storage/netstorage/add` now
|
||||||
|
verifies the share END-TO-END before anything is registered, and rolls everything back on failure.
|
||||||
|
Built on SPIKE-nas-verify-2026-07-11 (b57f6ca) with agent v0.81.0; live-validated A–E on demo 9201
|
||||||
|
against an isolated sim NAS.
|
||||||
|
|
||||||
|
- **Orchestration job** (`internal/web/netstorage_job.go`, the migrate.go shape): sync validation →
|
||||||
|
detached single-flight job on `context.Background()` (~150 s budget; a closed tab can't abort a
|
||||||
|
rollback) with phases `agent_add → verifying → probing → registering → done|failed`, polled on
|
||||||
|
NEW `GET /api/storage/netstorage/add/status`. Registration is the LAST step — the worst crash
|
||||||
|
outcome is an agent-side orphan, never a registered-but-broken path. Verify-lost after an agent
|
||||||
|
restart (`phase:none`) ⇒ controller rollback (Scenario F).
|
||||||
|
- **In-guest uid-1000 write probe** (`netprobe*.go` + hidden `--netprobe <dir>` re-exec mode in
|
||||||
|
main.go): `SysProcAttr.Credential{1000,1000}`, no shell; dot-file + nonce + readback + delete;
|
||||||
|
exit codes → `not_writable` (the squash trap — an export that mounts but denies uid-1000 writes
|
||||||
|
can no longer register) / `probe_io`; cleanup-fail = WARN on success, not a failure.
|
||||||
|
- **agentapi**: `AddNetStorage` result gains `verify/job_id/code`; typed `NetAddRefusedError`
|
||||||
|
(categorized sync refusals — unreachable pre-probe); new `NetVerifyStatus` (short GET, the 15 s
|
||||||
|
global client timeout is untouched — the long wait lives in the poll loop).
|
||||||
|
- **§3.2 Hungarian error map** server-side (`netAddMessage`): unreachable / nfs_export (MERGED
|
||||||
|
not-found+not-permitted — NFSv4 returns identical strings) / smb_auth / smb_share / timeout /
|
||||||
|
not_writable (the Route-A guidance with the computed uid+100000) / probe_io / generic.
|
||||||
|
- **Orphan surfacing**: any agent-configured share NOT in the registry renders as a remove-only
|
||||||
|
"Árva megosztás" row (closes the crash-window gap visibly; re-add with the same name = repair).
|
||||||
|
- **storage_network.html full redesign** on the canonical `storage_attach` pattern — kills the
|
||||||
|
`<details>/<summary>`-as-button hack and the NONEXISTENT `form-row`/`form-input` classes (the
|
||||||
|
unstyled-look root cause). SMB listed FIRST (`SMB (Synology, QNAP — a legtöbb NAS)`), NFS
|
||||||
|
two-recipe guidance (map-all-users simple recipe + full-fidelity `anonuid=<uid+100000>` with a
|
||||||
|
live computed host-id), staged poll progress (Kapcsolódás → Csatolási teszt → Írásteszt →
|
||||||
|
Regisztrálás), categorized errors + collapsible raw detail. Gates green; C8 render smoke guards
|
||||||
|
the class regression.
|
||||||
|
- Feature doc: `felhom.eu/documentation/controller/network-storage-nas.md` (authoritative).
|
||||||
|
Companion: agent v0.81.0 (retry=0, journal classifier, agent-side auto-rollback), host-install
|
||||||
|
v1.13.0 (`systemd-journal` group). Red-proof outcomes: REPORT.md.
|
||||||
|
|
||||||
### v0.112.0 — self-update without credentials: anonymous registry mode (2026-07-10)
|
### v0.112.0 — self-update without credentials: anonymous registry mode (2026-07-10)
|
||||||
|
|
||||||
Root cause (live on Peti's box): the updater piggybacked on the Git Sync credentials and REFUSED when
|
Root cause (live on Peti's box): the updater piggybacked on the Git Sync credentials and REFUSED when
|
||||||
|
|||||||
+21
-1
@@ -7,7 +7,27 @@
|
|||||||
>
|
>
|
||||||
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
|
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
|
||||||
|
|
||||||
Last updated: 2026-07-10 (v0.112.0 — anonymous registry self-update; pairs with hub v0.43.1)
|
Last updated: 2026-07-11 (v0.113.0 — NAS verify-before-commit + page redesign; pairs with agent v0.81.0)
|
||||||
|
|
||||||
|
> **2026-07-11 — v0.113.0: NAS verify-before-commit + page redesign (LIVE on 9201, pairs with agent
|
||||||
|
> v0.81.0 + host-install v1.13.0).** `POST /api/storage/netstorage/add` no longer registers blind
|
||||||
|
> (the bogus-share-at-Készenlét bug is dead): detached single-flight orchestration
|
||||||
|
> (`internal/web/netstorage_job.go`, migrate shape; poll `GET .../add/status`) = agent add (units +
|
||||||
|
> agent-side detached verify with journal classification + auto-rollback) → controller **uid-1000
|
||||||
|
> re-exec write probe** (`--netprobe`, SysProcAttr.Credential — catches the squash trap) → register
|
||||||
|
> LAST. Any failure = full rollback; verify-lost after agent restart ⇒ controller rollback; unregistered
|
||||||
|
> agent shares surface as remove-only "Árva megosztás" rows. §3.2 Hungarian error map server-side
|
||||||
|
> (`netAddMessage`; `nfs_export` MERGES not-found/not-permitted — NFSv4 identical strings).
|
||||||
|
> storage_network.html rebuilt on the storage_attach pattern (form-row/form-input killed), SMB listed
|
||||||
|
> first, NFS two-recipe guidance with live computed uid+100000. Agent v0.81.0: NFS `retry=0`
|
||||||
|
> (dead-NAS access 91 s→3.8 s), `ClassifyNetVerifyFailure` (Q4-verbatim), unprivileged journal read
|
||||||
|
> (systemd-journal group — host-install v1.13.0 adds it; NO new sudoers). Live-validated A–E on 9201
|
||||||
|
> vs an isolated sim NAS (all transcripts + red-proofs in REPORT.md); Route A proven in production
|
||||||
|
> (alien-uid squash → server-side 1060:1060). Authoritative doc:
|
||||||
|
> felhom.eu/documentation/controller/network-storage-nas.md. NOT published (0.81.0 not in Gitea /
|
||||||
|
> Day-0 manifest; Peti untouched — his rollout incl. the usermod one-liner comes with the floor bump).
|
||||||
|
> Gotcha for future sessions: the controller container is bridge-only — in-guest API tests need the
|
||||||
|
> CONTAINER IP + `Host: felhom.demo-felhom.eu` (127.0.0.1:8080 is stale advice).
|
||||||
|
|
||||||
> **2026-07-10 — v0.112.0: self-update without credentials (LIVE on 9201, pairs with hub v0.43.1).**
|
> **2026-07-10 — v0.112.0: self-update without credentials (LIVE on 9201, pairs with hub v0.43.1).**
|
||||||
> Root cause on Peti's box: the updater refused without Git Sync creds, but the public package is
|
> Root cause on Peti's box: the updater refused without Git Sync creds, but the public package is
|
||||||
|
|||||||
@@ -1,68 +1,123 @@
|
|||||||
# REPORT — v0.112.0: self-update without credentials (anonymous registry mode)
|
# REPORT — v0.113.0: NAS verify-before-commit + page redesign + protocol-honest guidance
|
||||||
|
|
||||||
**Date:** 2026-07-10 · **Version:** v0.112.0 (from v0.111.0) · **Pairs with:** hub v0.43.1
|
**Date:** 2026-07-11 · **Version:** controller v0.113.0 (from v0.112.0) · **Pairs with:** agent
|
||||||
**Spec:** TASK — controller self-update without credentials (anonymous registry mode)
|
v0.81.0 + felhom.eu host-install v1.13.0
|
||||||
|
**Spec:** TASK — NAS verify-before-commit + page redesign + protocol-honest guidance
|
||||||
|
**Evidence base:** `felhom.eu/documentation/audits/SPIKE-nas-verify-2026-07-11.md` (b57f6c1)
|
||||||
|
|
||||||
## Root cause (live on Peti's box)
|
## Confirmed baselines → shipped
|
||||||
|
|
||||||
The updater piggybacked on the Git Sync credentials and REFUSED when absent (`queryRegistry` +
|
| Repo | Baseline (`main`) | Shipped commits | Version |
|
||||||
`pullImage` guards) — but the registry serves the public package anonymously (Docker v2 token dance,
|
|---|---|---|---|
|
||||||
verified empirically 2026-07-10). A fresh customer without a private catalog silently lost version
|
| felhom-agent | `300f06722b` (v0.80.0) | `added9d` | **v0.81.0** — deployed on felhom-pve |
|
||||||
discovery + self-update for no reason. Credentials are now what they were meant to be: **optional,
|
| felhom-controller | `3db9126121` (v0.112.0) | `bb8737a` (orchestration) + `a65dcff` (UI) + this docs commit | **v0.113.0** — live on 9201 |
|
||||||
private-catalog only**.
|
| felhom.eu | `e80e14d6` | `27e2fb0` | host-install **v1.13.0** + feature doc |
|
||||||
|
|
||||||
## What shipped
|
## What shipped (files)
|
||||||
|
|
||||||
- **Part 1 — `queryRegistry` anonymous mode** (`internal/selfupdate/updater.go`): both creds empty →
|
**Agent (`added9d`)** — `internal/storage/netmount.go` (NFS `retry=0` + exported
|
||||||
plain GET; on 401 parse the `WWW-Authenticate` Bearer challenge (realm + service **from the header**
|
`NetworkMountedAt`/`NetworkEndpointReachable`), NEW `internal/storage/netverify.go`
|
||||||
— never hardcoded; quoted/bare values, any order, commas-in-quotes handled); GET the realm with
|
(`ClassifyNetVerifyFailure`, Q4-verbatim table), NEW `internal/localapi/netverifyjob.go` (in-memory
|
||||||
`service` + `repository:<image>:pull` scope and NO credentials; retry tags/list with the Bearer.
|
single-slot detached verify + auto-rollback + `GET /netstorage/verify-status`),
|
||||||
Creds present → the BasicAuth path unchanged. Half-configured pair → loud
|
`internal/localapi/netstorage.go` (sync fast-fail: full validation + 2 s TCP pre-probe before ANY
|
||||||
"hiányos registry hitelesítő adatok". A genuinely-denying registry →
|
install; verify-job start), `server.go` (seams + route). **No new sudoers grants** — journal read is
|
||||||
**"registry denied anonymous access — a private registry requires Git Sync credentials"** (never the
|
unprivileged via the `systemd-journal` group.
|
||||||
old "credentials missing"). Registry base URL now derives from the image ref (was a hardcoded host).
|
|
||||||
- **Part 2 — `pullImage` without login:** no creds → the `docker login` step skipped entirely
|
|
||||||
(docker's native anonymous flow covers public packages); creds → login/pull/logout unchanged
|
|
||||||
(token still stdin-only). The old refuse-guard removed; a denied anonymous pull surfaces docker's
|
|
||||||
own error.
|
|
||||||
- **Part 3 — settings truthfulness:** "Verzió és frissítés" gains
|
|
||||||
"Registry: nyilvános (hitelesítés nélkül)" / "Registry: hitelesített"; credential-less is no longer
|
|
||||||
an error state; the Hiba row appears only on real failure. `DryRun.PullCapable` counts anonymous as
|
|
||||||
capable (false only on a half-configured pair).
|
|
||||||
- **Part 4 (hub v0.43.1):** Git Sync form hint — "Opcionális — csak privát alkalmazás-katalógushoz.
|
|
||||||
A verziófrissítés enélkül is működik." (one template string).
|
|
||||||
|
|
||||||
## Tests + red-proof (executed; failures verbatim)
|
**Controller (`bb8737a` + `a65dcff`)** — `internal/agentapi/client.go` (verify fields, typed
|
||||||
|
`NetAddRefusedError`, `NetVerifyStatus`), NEW `internal/web/netprobe.go` + `netprobe_linux.go` +
|
||||||
|
`netprobe_other.go` (uid-1000 re-exec probe; `--netprobe` hidden mode in `cmd/controller/main.go`),
|
||||||
|
NEW `internal/web/netstorage_job.go` (detached single-flight orchestration + §3.2 Hungarian map),
|
||||||
|
`netstorage_handlers.go` (job-start add + status endpoint + orphan rows + `netListFn` seam),
|
||||||
|
`storage_handlers.go` (route), `templates/storage_network.html` (full redesign).
|
||||||
|
|
||||||
- **Red-proof** — the old creds-required guard restored in `queryRegistry` → all three anonymous tests
|
**felhom.eu (`27e2fb0`)** — `scripts/felhom-host-install.sh` v1.13.0 (`usermod -aG systemd-journal
|
||||||
FAILED with the old message visible:
|
felhom-agent`, idempotent; header/const drift v1.11.0-vs-1.12.0 fixed), NEW
|
||||||
`anonymous queryRegistry failed: registry hitelesítő adatok hiányoznak`. Restored → green.
|
`documentation/controller/network-storage-nas.md` (authoritative feature doc).
|
||||||
- Non-hollow coverage (`registry_anon_test.go`, httptest fake registry + fake CLI runner): full token
|
|
||||||
dance with ZERO creds (token request carries NO Authorization, correct
|
|
||||||
`service`+`scope=repository:admin/felhom-controller:pull`, highest semver returned, non-semver tags
|
|
||||||
skipped); creds path unchanged (BasicAuth on tags/list, token endpoint never hit); BOTH denial paths
|
|
||||||
(token 401, tags-401-with-Bearer) → the new clear error and NOT the old one; `parseWWWAuthenticate`
|
|
||||||
table (quoted/bare/order/comma-inside-quotes/missing-realm/non-Bearer/empty); pull with no creds →
|
|
||||||
**no login/logout invocation recorded**, exactly one `docker pull`; creds → login→pull→logout order +
|
|
||||||
token via stdin; partial creds refuse everywhere with zero docker commands run.
|
|
||||||
- Green gate both repos (`go build && go vet && go test ./...`) + controller template gates
|
|
||||||
(`template_id_gate.py`, `emoji_gate.py`) pass.
|
|
||||||
|
|
||||||
## Deploy + live validation (demo, credential-less for real)
|
## Tests + companion red-proofs (all: mutation run → FAIL observed → reverted → suite green)
|
||||||
|
|
||||||
- Controller `0.112.0` → 9201 (bootstrap mechanism), `Up (healthy)`. Hub `0.43.1` → GitOps sync,
|
Test funcs added: agent storage +3 (netverify_test.go incl. the pure `networkMountedIn` table),
|
||||||
Synced/Healthy, rollout complete.
|
agent localapi +5 (netverifyjob_test.go), controller web +9 (netstorage_job_test.go +
|
||||||
- The demo box's `controller.yaml` has `git.username: ""` / `git.token: ""` (verified quoted-empty →
|
storage_network_template_test.go).
|
||||||
parses to empty strings) — a genuinely credential-less box.
|
|
||||||
- **Live anonymous check against the real registry:** `POST /api/selfupdate/check` (in-guest, via the
|
|
||||||
container IP + Host header, hub-API-key bearer) →
|
|
||||||
`{"ok":true, "current_version":"0.112.0", "latest_version":"0.112.0", "update_available":false}` —
|
|
||||||
NO error. Pre-0.112.0 this exact call failed with "registry hitelesítő adatok hiányoznak".
|
|
||||||
- **Settings page live:** renders `Registry — nyilvános (hitelesítés nélkül)` and
|
|
||||||
`Legújabb verzió — 0.112.0`; no Hiba row.
|
|
||||||
|
|
||||||
## Pending operator step (per spec — Peti's box untouched by this task)
|
| # | Test | Red-proof mutant | Observed failure (verbatim core) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| A1 | `TestMountOptions_NFSRetry0_SMBWithout` (+ NFS rendering test) | reverted `retry=0` | `NFS options missing retry=0 (Q4-vi): "vers=4.1,...,_netdev"` |
|
||||||
|
| A2 | `TestClassifyNetVerifyFailure` (Q4-verbatim table incl. merged `nfs_export` + empty-journal degradation) | exit-code classifier (`return mount_failed` — everything is rc=32) | every non-generic row: `= "mount_failed", want "unreachable"/...` — an exit-code mutant cannot split smb_auth/smb_share |
|
||||||
|
| A3 | `TestNetVerify_MountFailed_RollsBackAndClassifies` (+ journal-unavailable sibling) | rollback call dropped | `RemoveNetworkMount not called for the failed install: removed=[]` + `creds file must be removed` |
|
||||||
|
| A4 | `TestNetVerify_TruthTable` (§8: ReadDir-ok+unmounted=FAIL; EACCES+mounted=OK) | readability-based verdict (`terr == nil`) | BOTH rows failed: `a readable-but-unmounted path must FAIL verify, got done` / `unreadable-but-mounted must PASS, got failed` |
|
||||||
|
| A5 | `TestNetVerify_UnreachablePreProbe_InstallsNothing` | pre-probe skipped | `unreachable add: got 200 want 502` (the install would have run) |
|
||||||
|
| A6 | `TestNetVerify_SingleFlight_AndNoJobShape` | running-check dropped | `second add while verifying: got 200 want 409` |
|
||||||
|
| C1 | `TestNetAdd_HappyPath_RegisterOnlyAfterProbe` | register moved BEFORE the probe | `phase = failed (category=register_failed detail=... already registered)` + C2's `must NOT be registered (got 1 paths)` |
|
||||||
|
| C2 | `TestNetAdd_ProbeFail_RollsBackNotRegistered` | rollback dropped | `probe-fail must roll the agent install back: removes=[]` |
|
||||||
|
| C3 | `TestNetAdd_AgentVerifyFailed_MappedMessage` | (kill-surface shared with C4's mutant) | asserts the EXACT §3.2 merged nfs_export string + probe-not-run + no controller double-remove |
|
||||||
|
| C4 | `TestNetAdd_VerifyLost_RollsBack` | `none` treated as success | `probe must not run when the verify was lost` |
|
||||||
|
| C5 | `TestNetProbeChild` (ok / unwritable→2→not_writable / nonce-tamper→3→probe_io / cleanup-fail→4=OK+warn) | — (pure child body + verdict table; Credential wiring live-validated in Scenario C) | — |
|
||||||
|
| C6 | `TestNetAdd_SingleFlight` | acquire check dropped | `second add: got 200 want 409` |
|
||||||
|
| C7 | `TestNetStorage_OrphanRow` | orphan sweep disabled (registry-only filter) | `items = 1, want 2 (registered + orphan)` |
|
||||||
|
| C8 | `TestStorageNetworkTemplate_CanonicalClasses` (renders through the PRODUCTION template tree) | `form-input` reintroduced | `rendered page still contains the banned pattern "form-input"` |
|
||||||
|
|
||||||
After Viktor's floor bump rolls Peti to 0.112.0: delete the temporary Git Sync creds from Peti's hub
|
Green gates: agent `go build && go vet && go test ./...` PASS (one hit of the KNOWN
|
||||||
config → next config pull → Peti's settings page should show a clean "Registry: nyilvános
|
`TestGenerateRecoveryCode` wordlist flake — clean on re-run, pre-documented); controller full suite
|
||||||
(hitelesítés nélkül)" with zero credentials on the box.
|
PASS; `template_id_gate.py` + `emoji_gate.py` green.
|
||||||
|
|
||||||
|
## Deployed + verified
|
||||||
|
|
||||||
|
- **Agent v0.81.0** on felhom-pve: `.bak-0.80.0` kept; `usermod -aG systemd-journal felhom-agent`
|
||||||
|
applied BEFORE restart; `felhom-agent --version` → 0.81.0; `id felhom-agent` →
|
||||||
|
`groups=990(felhom-agent),999(systemd-journal)`; journal shows a clean start (enrolled drive
|
||||||
|
bound under shared parent, local-api listening, hub desired-state gen 10, no capability
|
||||||
|
degradation).
|
||||||
|
- **Controller v0.113.0** on 9201 (golden/bootstrap mechanism): `docker ps` →
|
||||||
|
`gitea.dooplex.hu/admin/felhom-controller:0.113.0 Up (healthy)`.
|
||||||
|
|
||||||
|
## Live validation (anti-F9: the exact endpoint the UI invokes)
|
||||||
|
|
||||||
|
Method: `curl` from inside guest 9201 to the controller container
|
||||||
|
(`http://172.17.0.2:8080` + `Host: felhom.demo-felhom.eu` — the dashboard is host-routed; the demo
|
||||||
|
has no password so auth/CSRF do not apply) — POST `/api/storage/netstorage/add` + status-poll, the
|
||||||
|
byte-identical server pipeline behind the UI; the residual is client-side rendering only.
|
||||||
|
Sim NAS = isolated `/srv/nas-spike3/*` on DooPlex (`.bak-nasspike3` backups; pre-counts exports=2,
|
||||||
|
smb_sections=6; NO iptables — the unreachable case used the ping+neigh-verified-unused
|
||||||
|
192.168.0.199). Throwaway users `spike3b` (uid 1060) + `spike3smb`; passwords never committed.
|
||||||
|
|
||||||
|
| Scenario | Result | Key evidence |
|
||||||
|
|---|---|---|
|
||||||
|
| **A** bogus export on a reachable server (THE bug) | **PASS** — failed in **4 s**, category `nfs_export`, the exact merged Hungarian message, the REAL journal in detail (`reason given by server: No such file or directory` — the unprivileged journal read works), list EMPTY, **zero spike3 units left on felhom-pve** — the "Készenlét forever" behavior is dead |
|
||||||
|
| **B** SMB wrong password / wrong share | **PASS** — `smb_auth` („Hibás SMB felhasználónév vagy jelszó.") vs `smb_share` (distinct message), both 4 s, **creds file gone after each**, no units |
|
||||||
|
| **C** squash trap (`anonuid=1000`, NO all_squash — mounts fine) | **PASS** — agent verify passed → **uid-1000 probe refused** → `not_writable` with the Route-A message incl. the computed **101000**; full rollback (no units/mounts/registration); **no probe file** left on the export |
|
||||||
|
| **D** happy paths | **PASS** — d1 (NFS anonuid=101000), d2 (NFS **Route A**, alien uid 1060), d3 (SMB plain user, no force user): all `done` in **4 s**, registered Schedulable, health `ok`. d2 production proof: the installed unit carries `retry=0`; a uid-1000 write shows guest-view `65534:65534` and lands **server-side `1060:1060`** |
|
||||||
|
| **E** unreachable IP | **PASS** — failed in **2.0 s** (the agent's sync TCP pre-probe), category `unreachable`, message names the IP, **nothing installed** |
|
||||||
|
| **G** single-flight | unit-proven (A6 + C6); not re-proven live — the 4 s happy windows make a live race impractical |
|
||||||
|
|
||||||
|
**Teardown verified:** d1/d2/d3 removed via the real remove endpoint (list empty); 180 restored from
|
||||||
|
`.bak-nasspike3` (post-counts exports=2, smb_sections=6, 0 spike3 exports, both users deleted,
|
||||||
|
scratch gone); felhom-pve: 0 spike3 mounts/units/creds, mountpoint dirs removed,
|
||||||
|
`systemctl reset-failed` cleared the residual failed-unit listings; guest 9201: 0 spike3 mounts,
|
||||||
|
controller healthy.
|
||||||
|
|
||||||
|
## NOT yet live-validated
|
||||||
|
|
||||||
|
- Guest-restart trigger survival (Q1c follow-up — needs a restart window; no guest restart allowed).
|
||||||
|
- Synology/QNAP appliance pass (virtual-dsm) — the sim-vs-real caveat stands.
|
||||||
|
- Peti rollout: floor bump + his `usermod -aG systemd-journal felhom-agent` step — when the NAS
|
||||||
|
feature reaches him. STOP honored: no publish train (agent 0.81.0 NOT published to Gitea, Day-0
|
||||||
|
manifest untouched), Peti's box untouched, guest 9201 never restarted.
|
||||||
|
- Scenario F live (agent restart mid-verify) — covered by unit tests A6/C4 + the orphan surface
|
||||||
|
(C7); a live kill inside a 4 s verify window is impractical.
|
||||||
|
|
||||||
|
## Observations (documented, NOT acted on)
|
||||||
|
|
||||||
|
- The demo host had NO pre-existing `nas-media` unit (the spike-era shares were fully torn down) —
|
||||||
|
nothing predating `retry=0` exists; the "don't rewrite installed units" rule was moot in practice.
|
||||||
|
- `RemoveNetworkMount` leaves the EMPTY mountpoint dir under `/mnt/felhom-drives/` and does not
|
||||||
|
`reset-failed` a failed mount unit's residual state (the unit FILES are removed; a `not-found
|
||||||
|
failed` listing lingers until reset-failed/reboot). Cosmetic; cleaned by hand this run.
|
||||||
|
- Pre-existing (since 2026-07-08, unrelated to this task): `lanresolver: cannot list provisioned
|
||||||
|
guests: permission denied` — `/var/lib/felhom-agent/guests` is root-0700 while the agent runs
|
||||||
|
non-root.
|
||||||
|
- host-install header/SCRIPT_VERSION drift (v1.11.0 vs 1.12.0): the v1.12.0 bump had shipped with
|
||||||
|
no changelog entry and no header sync; both fixed at v1.13.0.
|
||||||
|
- The controller container publishes NO ports (bridge-only, traefik-fronted): in-guest API testing
|
||||||
|
needs the container IP + `Host:` header — the "POST to in-guest 127.0.0.1:8080" note in older
|
||||||
|
session memory is stale.
|
||||||
|
|||||||
@@ -172,6 +172,8 @@
|
|||||||
| Interface | Defined in | Implemented by | Fakes/tests at |
|
| Interface | Defined in | Implemented by | Fakes/tests at |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `diskAgent` | controller/internal/web/storage_handlers.go | `*agentapi.Client` | `mockAgent` in controller/internal/web/storage_handlers_test.go |
|
| `diskAgent` | controller/internal/web/storage_handlers.go | `*agentapi.Client` | `mockAgent` in controller/internal/web/storage_handlers_test.go |
|
||||||
|
| `netAgent` + `Server.netAgentFn/netProbeFn/netListFn` | controller/internal/web/netstorage_job.go (+ server.go fields) | `*agentapi.Client` / `runNetProbe` (linux re-exec) / `agent.ListNetStorage` | `fakeNetAgent` + fn injections in controller/internal/web/netstorage_job_test.go — the NAS add orchestration never shells/TLS-dials in tests |
|
||||||
|
| `netProbeReadBack` (package var) | controller/internal/web/netprobe.go | `os.ReadFile` | overridden in TestNetProbeChild (nonce-tamper + cleanup-fail rows); package var because the child is a RE-EXEC'd process in production |
|
||||||
| `quiesce.Backend` / `quiesce.Stacks` | controller/internal/quiesce/quiesce.go | adapter over `*agentapi.Client` / `*stacks.Manager` | `fakeBackend`/`fakeStacks` in controller/internal/quiesce/quiesce_test.go |
|
| `quiesce.Backend` / `quiesce.Stacks` | controller/internal/quiesce/quiesce.go | adapter over `*agentapi.Client` / `*stacks.Manager` | `fakeBackend`/`fakeStacks` in controller/internal/quiesce/quiesce_test.go |
|
||||||
| `channelhealth.Probe` (func) + `Sink` | controller/internal/channelhealth/checker.go | `Server.ProbeAgentChannel` / notifier adapter | `fakeSink` in controller/internal/channelhealth/checker_test.go |
|
| `channelhealth.Probe` (func) + `Sink` | controller/internal/channelhealth/checker.go | `Server.ProbeAgentChannel` / notifier adapter | `fakeSink` in controller/internal/channelhealth/checker_test.go |
|
||||||
| `appbackup.StackDataProvider` | controller/internal/appbackup/appdata.go | `*stacks.Manager` (via `backup.SetStackProvider`) | `fakeRecoveryProvider` in controller/internal/backup/recovery_unit_test.go |
|
| `appbackup.StackDataProvider` | controller/internal/appbackup/appdata.go | `*stacks.Manager` (via `backup.SetStackProvider`) | `fakeRecoveryProvider` in controller/internal/backup/recovery_unit_test.go |
|
||||||
|
|||||||
@@ -798,6 +798,21 @@ not just those with HDD data. Non-HDD apps can configure destination, method, an
|
|||||||
> path server-side (`refuseNetworkLifecycle`). The **drive-absent gate** (`planDriveGates`) and the
|
> path server-side (`refuseNetworkLifecycle`). The **drive-absent gate** (`planDriveGates`) and the
|
||||||
> **missing-storage** surface **skip network paths**, so an `unreachable` NAS is a **recoverable warning**
|
> **missing-storage** surface **skip network paths**, so an `unreachable` NAS is a **recoverable warning**
|
||||||
> (`networkStorageWarnings` → a distinct app-card badge), **never** the drive "missing → stopped" cascade.
|
> (`networkStorageWarnings` → a distinct app-card badge), **never** the drive "missing → stopped" cascade.
|
||||||
|
> - **Verify-before-commit (v0.113.0, pairs with agent v0.81.0; SPIKE-nas-verify-2026-07-11):** add no
|
||||||
|
> longer registers blind. `POST /api/storage/netstorage/add` returns `{started:true}` and a DETACHED
|
||||||
|
> single-flight orchestration job (`internal/web/netstorage_job.go`, migrate.go shape; polled on
|
||||||
|
> `GET /api/storage/netstorage/add/status`) drives `agent_add → verifying → probing → registering`:
|
||||||
|
> the agent installs the units + runs its own detached mount-verify (journal-classified, agent-side
|
||||||
|
> auto-rollback), then the controller re-execs itself as **`--netprobe <dir>` at uid/gid 1000**
|
||||||
|
> (`SysProcAttr.Credential`, `netprobe*.go`) to prove a media app can WRITE through the share (the
|
||||||
|
> squash trap), and only then registers. ANY failure = full rollback (nothing registered, nothing
|
||||||
|
> installed, no creds file); categorized Hungarian errors (`netAddMessage`, §3.2 map — note
|
||||||
|
> `nfs_export` merges not-found/not-permitted: NFSv4 returns identical strings). An agent restart
|
||||||
|
> mid-verify (`verify-status` phase `none`) ⇒ controller rollback; any agent-configured share NOT in
|
||||||
|
> the registry surfaces as a remove-only **"Árva megosztás"** orphan row. The page
|
||||||
|
> (`storage_network.html`) is on the canonical form pattern with staged poll progress and the
|
||||||
|
> protocol-honest NAS guidance (SMB-first; NFS map-all-users vs full-fidelity `anonuid=<uid+100000>`).
|
||||||
|
> Authoritative doc: `felhom.eu/documentation/controller/network-storage-nas.md`.
|
||||||
> - **Limits (v1):** a share's `+100000` uid mapping is fixed at add-time (one app / same-uid apps); for
|
> - **Limits (v1):** a share's `+100000` uid mapping is fixed at add-time (one app / same-uid apps); for
|
||||||
> write apps on a soft NFS mount, an in-flight file can truncate if the NAS vanishes mid-write (prefer
|
> write apps on a soft NFS mount, an in-flight file can truncate if the NAS vanishes mid-write (prefer
|
||||||
> atomic-write apps / SSD-staging).
|
> atomic-write apps / SSD-staging).
|
||||||
|
|||||||
Reference in New Issue
Block a user