diff --git a/docs/settings-plan-audit.md b/docs/settings-plan-audit.md index 2dc510c..2050201 100644 --- a/docs/settings-plan-audit.md +++ b/docs/settings-plan-audit.md @@ -109,7 +109,7 @@ Round 1 critics read the first full plan, round 2 the revision that answered the | 1 | Simplicity | minor | The read-aloud test is scheduled only in Phase 5. | ADOPTED | P1 exit criterion: a newcomer explains Overview, Computers and Help back in 20 minutes; D9 adds the same for Review & Apply, the Spotify section and one key editor at the end of P2. | | 1 | Simplicity | minor | Routine code updates still need MacBook, scp, ssh install.sh; no "Update program" path on the page. | SIMPLIFIED | K13 "Install the staged folder" from the page as a one-shot launchd job; the staged folder still arrives by scp (no git credential on Ingest); pull_site.py is replaced by Export site bundle (K17). | | 1 | Simplicity | minor | Schedules offers an editable "Alarms are armed" window; the Mute button does not say what it does. | ADOPTED | K9 one schedule and "alarms are always on" as one sentence everywhere; K4 button "Silence alarms until midnight". | -| 1 | Safety | blocker | The settings server is unauthenticated remote code execution on every private range with no CSRF defence; a draft's remote.cmd runs before Apply via --once and the probes. | SIMPLIFIED | K2 one editing code (keychain, lockout, origin check on writes); K6 command edits are red in Review and need the code again; K8 probes run a fixed set with quoted parameters; there is no --once preview. The address allow-list was cut (C3). Residual: one code over plain HTTP on the LAN. | +| 1 | Safety | blocker | The settings server is unauthenticated remote code execution on every private range with no CSRF defence; a draft's remote.cmd runs before Apply via --once and the probes. | SIMPLIFIED | K2 one editing code (a setting in the repo's config, lockout, origin check on writes); K6 command edits are red in Review and need the code again; K8 probes run a fixed set with quoted parameters; there is no --once preview. The address allow-list was cut (C3). Residual: one code over plain HTTP on the LAN. | | 1 | Safety | major | Every Apply kickstarts the watchdog with no SIGTERM handler: a fix in flight is killed (HyperDeck left disabled), cooldowns, acks and tries lost, UI-only edits restart too. | ADOPTED | K12 SIGTERM handler lets the fix finish, fixes-running.json, state.json persists acks/tries/cooldowns, the HyperDeck re-enable sits in a finally; K10 waits for no fix and page-only changes skip the restart. The SIGHUP alternative was cut (C1). | | 1 | Safety | major | Apply success and rollback hinge on sp_hb, which needs Companion: spurious rollbacks and a loop with no terminal state. | ADOPTED | K10 success = started.json newer than T with our sha, no traceback, alive after 10 s; rollback once; red notice until the next good Apply. | | 1 | Safety | major | "Test this fix now" runs outside the watchdog, bypassing the recording guard, cooldown and logging; it can PoE-cycle. | ADOPTED | K7 test verb through the watchdog with the recording guard, cooldown and TEST logging; start/reconnect kinds only. | @@ -137,7 +137,7 @@ Round 1 critics read the first full plan, round 2 the revision that answered the | 2 | Completeness | minor | spotify.name has a field but nothing reads it; labels keep the old playlist name. | ADOPTED | K12 reads spotify.{playlist,name,open_wait,play_wait}; K9 one link field plus name; the K6 Play builder derives the label. | | 2 | Completeness | minor | Add or remove a HyperDeck leaves the kept decks' optional flag untouched. | ADOPTED | P4: the job asks per kept deck "may it have no disk?". | | 2 | Completeness | minor | hosts.ssh is classed R, but the MIDI streams and pollers keep the old address after SIGHUP. | CUT | C1: no partial reload; every Apply restarts the watchdog. | -| 2 | Completeness | minor | The edit key written to the login keychain by install.sh over ssh may fail silently. | ADOPTED | K2: the agent makes the code on first start in the GUI session, with a 0600 file fallback. | +| 2 | Completeness | minor | The edit key written to the login keychain by install.sh over ssh may fail silently. | SUPERSEDED | The user chose (2026-10-13) to keep the code in config.json in the private repo (K2); nothing is written to the keychain. | | 2 | Completeness | minor | The surfaces light has no data source. | CUT | C11: surfaces are not shown. | | 2 | Completeness | minor | A changed SSH host key needs a Terminal step. | ADOPTED | K8 Setup tab: Forget the old login key. | | 2 | Completeness | minor | The nav walk stops at the first 404; a new page never prompts a re-run. | SIMPLIFIED | C6 drops periodic detection; K9 shows "coloured on N of M pages" when the section opens, with Colour them now and Undo; the walk rule is a P1 spike item. | @@ -173,7 +173,7 @@ Round 1 critics read the first full plan, round 2 the revision that answered the | 2 | Safety | minor | Hard validation needs Companion up; resetControl to blank a cell is untested. | ADOPTED | K10 soft live warnings; P1 spike "resetControl blanks a cell?" with a by-hand fallback. | | 2 | Safety | minor | Stop, Revert nav, Prune, Re-import and Install have no confirmation; Mute sits under the guard. | ADOPTED | C3 one dialog style stating the consequence, a typed word for Stop and Emergency; K11 Silence alarms is never guarded. | | 2 | Safety | minor | The --once preview triggers a TCC prompt; the ATEM snapshot opens a second client with no guard. | SIMPLIFIED | There is no --once preview in K10; "Check the switcher now" is in K11's guarded list. The ATEM client limit is not noted beside the button. | -| 2 | Safety | minor | wmic is gone on Windows 11 24H2; the edit key is made over ssh; Pull needs a stored git credential. | ADOPTED | K2 agent-made code; K13 no git credential and no Pull button; D4 Windows probes use Get-CimInstance and schtasks /Query /FO CSV, never wmic. | +| 2 | Safety | minor | wmic is gone on Windows 11 24H2; the edit key is made over ssh; Pull needs a stored git credential. | ADOPTED | K2 the code is a config setting in the repo; K13 no git credential and no Pull button; D4 Windows probes use Get-CimInstance and schtasks /Query /FO CSV, never wmic. | | 2 | Safety | minor | The nav walk stops at a gap; hand-moved pages are not detected. | SUPERSEDED | Round 3 Feasibility put the walk rule in the P1 spike; periodic detection was cut (C6) in favour of the K9 count plus Colour them now. | | 2 | Feasibility | major | The R apply class cannot work: start_pollers and Engine capture cfg once. | CUT | C1. | | 2 | Feasibility | major | The 30 s graceful stop exceeds launchd's 5 s exit timeout; fix threads are anonymous daemon threads. | ADOPTED | K12 ExitTimeOut derived at install, a thread registry behind fixes-running.json, join at most ExitTimeOut-5; K16 SIGTERM mid-fix test. | @@ -214,7 +214,7 @@ Round 1 critics read the first full plan, round 2 the revision that answered the | 3 | Simplicity | minor | Add a MIDI link is hidden under More jobs. | ADOPTED | K4 three job buttons. | | 3 | Simplicity | minor | The "Not on the deck" tray is unexplained. | ADOPTED | K5 tray labelled "Shown inside the RF group (no key of their own)". | | 3 | Simplicity | minor | Watchdog controls in two places, two magic words, second confirmations. | ADOPTED | C3 one confirmation style; D6 Start / Stop / Restart the watchdog live on Overview only, Stop behind the typed word. | -| 3 | Safety | blocker | No loopback listener, so /edit-key is unreachable and nobody can ever switch Editing on. | ADOPTED | K1 binds 127.0.0.1 always; K2 serves /edit-key only there; K16 loopback-only test. | +| 3 | Safety | blocker | No loopback listener, so /edit-key is unreachable and nobody can ever switch Editing on. | SUPERSEDED | There is no /edit-key page: the code is a config setting in the repo (K2, the user's choice 2026-10-13); K1 still binds 127.0.0.1 always. | | 3 | Safety | blocker | setsid does not exist on macOS; the installer dies with the agent's process group half-way. | ADOPTED | K13 one-shot launchd job, AbandonProcessGroup on the settings plist, the watchdog bootstrapped first. | | 3 | Safety | major | safe_target's name shortcut silently wipes hand-added keys. | ADOPTED | K10 marker-only rule; Review lists foreign keys by position; Apply refuses. | | 3 | Safety | major | Raw commands behind one plaintext reusable code; no lockout; the allow-list off by default. | SIMPLIFIED | K2 lockout (5 tries, 10 min) and origin check; K6 Custom commands are red and re-ask the code. Admin code, RUN word and allow-list were cut (C3). Residual: one code on a LAN is a remote-command surface. | @@ -230,7 +230,7 @@ Round 1 critics read the first full plan, round 2 the revision that answered the | 3 | Feasibility | major | cmd.exe %ERRORLEVEL% batching always reports OK. | ADOPTED | K12 (the same fix as round 3 Completeness). | | 3 | Feasibility | major | A (key, type, sub) identity collides on the MIDI key; the test verb cannot pick a peer. | ADOPTED | K12 per-type identity (midi_peer = host, session, peer; remote = host, cmd; ...); K7 the verb carries it. | | 3 | Feasibility | major | test_apply_class contradicts the R rows. | CUT | C1. | -| 3 | Feasibility | major | The LAN-only bind leaves /edit-key unreachable; interface names cannot be bound, only addresses. | ADOPTED | K1 and D5: bind by address (127.0.0.1 always, the only listener serving /edit-key, plus each LAN interface's IPv4), re-checked every minute and rebound on change. | +| 3 | Feasibility | major | The LAN-only bind leaves /edit-key unreachable; interface names cannot be bound, only addresses. | ADOPTED | K1 and D5: bind by address (127.0.0.1 always, plus each LAN interface's IPv4), re-checked every minute and rebound on change. | | 3 | Feasibility | major | install.sh copies atem-reference.xml (and nav_locations, built pages) over the data dir. | ADOPTED | K13 never overwrites site-owned files without --config, --site or --force-site-files, with a diff notice. | | 3 | Feasibility | major | Phase 0b and Phase 2 bundle too much to test as single steps. | SIMPLIFIED | C1, C2, C4, C7 and C10 remove most of the load; P0 and P2 stay single phases (about 1.5 and 4 days). | | 3 | Feasibility | minor | --skip-spotify is a no-op; the Preview shows unjudged keys green. | SIMPLIFIED | There is no preview run in K10, so there are no flags to get wrong. | @@ -241,7 +241,7 @@ Round 1 critics read the first full plan, round 2 the revision that answered the | 3 | Feasibility | minor | The self record is referenceable by ssh fixes. | ADOPTED | K12 (the same fix as round 3 Completeness). | | 3 | Feasibility | minor | Tailing a rotating log can miss the lines and trigger a false rollback. | ADOPTED | K10 and K12 started.json; the log is read only for tracebacks. | -Disposition totals over the 157 findings: ADOPTED 115, SIMPLIFIED 20, CUT 13, SUPERSEDED 8, NOT A GAP 1. +Disposition totals over the 157 findings: ADOPTED 113, SIMPLIFIED 20, CUT 13, SUPERSEDED 10, NOT A GAP 1. ## Deliberate cuts and why @@ -277,7 +277,7 @@ C14, preview run. Review has no "Preview the deck with these settings" button th - Every Apply restarts the watchdog (C1): about five seconds of purple deck, and holds pressed in that gap are dropped. Mitigation: Apply is refused during live services (K11), page-only changes skip the restart (K10), Review says it will happen, the SIGTERM handler lets a running fix finish (K12) and sp_cmd is cleared and logged at start. - No queue (C2): a change wanted during a service waits for a person to come back and press Apply. Mitigation: the draft is saved, Review says when the service ends, the Emergency override (typed LIVE plus a reason, logged) exists, and Q2 pins the windows. -- Raw Custom commands (K6) are a remote-command surface on seven computers, guarded by one code over plain HTTP on a LAN. Mitigation: LAN-only binding narrowed by hosts.ingest.networks (K1), origin check and lockout (K2), the code asked again and the command shown red in Review (K6), zsh -n / sh -n, History naming the editor (K14), and Q1 limiting who holds the code. +- Raw Custom commands (K6) are a remote-command surface on seven computers, guarded by one code over plain HTTP on a LAN. Mitigation: LAN-only binding narrowed by hosts.ingest.networks (K1), origin check and lockout (K2), the code asked again and the command shown red in Review (K6), zsh -n / sh -n, History naming the editor (K14), and the private repo's access list deciding who holds the code. - Probes are parameterised, not fixed (K8): names and paths from the config reach ssh lines. Mitigation: shlex-quoted and regex-checked parameters, a fixed command set, the metacharacter test (K16), never run during a fix, 6 s timeout, never wake a PC. - Windows DefaultShell (K12): the batched checks assume cmd.exe. Mitigation: a light on the PC's card and refusal of cmd-batched checks when the shell is not cmd; verified once on the Lighting PC; setting DefaultShell stays a by-hand system change for the user. - Python 3.13 removes telnetlib, which netgear.py uses (K12 self-check for 3.9-3.12). Mitigation: the self-check light; a newer macOS at another campus would lose the Dante map and PoE cycling until telnetlib is replaced, which is not in this plan. @@ -289,7 +289,7 @@ C14, preview run. Review has no "Preview the deck with these settings" button th ## Still open (needs the user) -- Q1 Who gets the editing code besides you? -- Q2 Live services at NA: only Sunday 06:30-13:30, or also midweek services or rehearsals that changes must wait for? -- Q3 The other campuses: their names, and does each have a Mac that runs Companion (where the page and the watchdog would live)? -- Q4 Unwatched Companion connections at NA (Govee x7, DiGiCo_OSC, m32, AbleSet, tcp-HyperDeck-1..6): leave them? +- Q1 Answered 2026-10-13: the editing code lives in config.json in the private repo; everyone with the repo has it. +- Q2 Still open: the page refuses to apply changes while a service is on (Sunday 06:30-13:30 today, plus any time a deck is recording). Are there other times, such as a midweek service or a rehearsal, when it should refuse too? +- Q3 Answered 2026-10-13: Apollo Beach, Woonsocket and Southcoast, each with a Mac Studio already running Companion. +- Q4 Answered 2026-10-13: the unwatched connections stay unwatched. diff --git a/docs/settings-plan-details.md b/docs/settings-plan-details.md index 8318bcc..f0725f1 100644 --- a/docs/settings-plan-details.md +++ b/docs/settings-plan-details.md @@ -19,7 +19,7 @@ Values are typed once: every string that names a program, path, task or helper l | `site.id` NEW | Site setup > Site (Advanced) | Site id | Short name for folders and files ("na"). | `[a-z0-9_-]+`; restart | | `site.name`, `site.ingest_name`, `site.ingest_hostname`, `site.ingest_ips[]` NEW | Site setup > Site | Site name, this Mac's name, network name, addresses | Used in log headers, alert text, key notes and generated setup scripts (replaces "NA Ingest" and the IPs in windows-setup.ps1). [Check this Mac's network ports] fills the addresses. | non-empty; IPs valid; restart (a changed note alone never forces a re-import, D11) | | `site.settings_port` NEW | Site setup > Site (Advanced) | Port | Where this page listens: loopback plus every private-address port, narrowed by this Mac's LAN list. Default 8020. | 1024-65535; the dialog states the consequence (the page moves to the new address); settings | -| editing code (keychain item `status-settings-edit-key`, 0600 file fallback `edit-key` in the data dir) NEW | Site setup > Site (Advanced) | "editing code: set ", [Make a new editing code] | Random word-list code the settings agent makes on its first start in the logged-in session; shown only on this Mac's screen at `http://127.0.0.1:8020/edit-key`; browsers remember it with the person's first name. The one code: no admin code, no address list. | never shown over the LAN; the dialog says every browser must enter the new code; settings | +| `site.editing_code` NEW | Site setup > Site (Advanced) | Editing code | The code that switches Editing on. It is a setting in this campus's `config.json`, kept in the private git repo, so everyone with the repo has it (the user's choice, 2026-10-13); browsers remember it with the person's first name. Masked in every API response unless the request carries it. The one code: no admin code, no address list, no page-generated code. | non-empty; changing it: the dialog says every browser must enter the new code; none | | `site.live_services[]` NEW | Site setup > Schedules | Live services | When a service is on: Apply, Run now, Restart the watchdog, Re-import, Check the switcher and Install are refused and your draft stays saved; auto-fix may run; alarms are always on. Defaults to `auto_fix.windows`; the Phase 0 migration writes it. | days in mon..sun; `HH:MM`; end <= start = runs past midnight (allowed); warn if empty; restart (ServiceMode is built at start) | | built-in live signal: a watched HyperDeck that "follows the recording" is recording | Site setup > Schedules ("Also counts as live", read-only line with "Live now?") | - | Derived from every hyperdeck check with "follows the recording"; the watchdog writes `live-now.json` on change / every 5 s with a timestamp; older than 30 s = schedule only (said in Review). No connection/variable/value triple on the page; "OBS is streaming" is added as a built-in kind only if the Phase 1 spike finds the module variable. | DNE; warn when no watched deck follows the recording | | `site.contacts` NEW | Site setup > Site | Contacts | Printed on the cheat sheet. | free text; none | @@ -193,12 +193,12 @@ The other NA records: **prop** (sshd, DVS daemon NA-ProPresenter-Mac, helper_dvs ## D. Safety and access -- **Who can read and change what.** The server binds IP addresses, not interface names: at start it resolves the IPv4 address of each LAN-use interface in `hosts.ingest.networks` from `ifconfig` (every private-address interface when the list is empty, with a grey notice), re-checks every minute and rebinds on change; 127.0.0.1 is always bound and is the only listener that serves `/edit-key`; never 0.0.0.0, and it answers only private ranges (10/8, 172.16/12, 192.168/16, 169.254/16, 127/8); everything else gets 403. **Everything is readable on the LAN without the code**: Overview, Keys, Computers (inventories, generated scripts, the Dante map), Logs, History (list and bodies), the key guide, Help and every Site setup screen, with the alert web hook masked. **Every change needs the editing code** (`X-Edit-Key` header equal to the code in this Mac's keychain item `status-settings-edit-key`, with the 0600 file fallback), a `Host`/`Origin` header naming this server (CSRF), and a name; lockout for 10 minutes after 5 wrong tries; every write is logged with address and name and History records "changed by from
". The code is shown only at `http://127.0.0.1:8020/edit-key` on the Ingest Mac's own screen (404 on a LAN socket) and is rotated from Site setup > Site > Advanced. Changing the port or the code shows a dialog that states the consequence. There is no admin code, no address allow-list and no second code; the editing code is not a password for any other system and is never typed by Claude. +- **Who can read and change what.** The server binds IP addresses, not interface names: at start it resolves the IPv4 address of each LAN-use interface in `hosts.ingest.networks` from `ifconfig` (every private-address interface when the list is empty, with a grey notice), re-checks every minute and rebinds on change; 127.0.0.1 is always bound; never 0.0.0.0, and it answers only private ranges (10/8, 172.16/12, 192.168/16, 169.254/16, 127/8); everything else gets 403. **Everything is readable on the LAN without the code**: Overview, Keys, Computers (inventories, generated scripts, the Dante map), Logs, History (list and bodies), the key guide, Help and every Site setup screen, with the alert web hook masked. **Every change needs the editing code** (`X-Edit-Key` header equal to `site.editing_code` in the live config), a `Host`/`Origin` header naming this server (CSRF), and a name; lockout for 10 minutes after 5 wrong tries; every write is logged with address and name and History records "changed by from
". The code lives in `config.json` in the private git repo, so everyone with the repo has it; it is masked in every response unless the request carries it, and is changed from Site setup > Site > Advanced or in the repo. Changing the port or the code shows a dialog that states the consequence. There is no admin code, no address allow-list and no second code; the editing code is not a password for any other system and is never typed by Claude. - **Commands never run from the page before Apply.** `fixes.*.run`, `remote.cmd`, `process.match` and `pref.*` are "command edits": generated by builders, shown in red in Review, executed only by the watchdog after Apply; a raw Custom command (Advanced) asks for the editing code again before it is accepted into the draft. Probes run only the fixed set in `probes.py` with shlex-quoted / regex-checked parameters. "Run this fix now" never calls `execute_fix` in the settings process: it sends `test