docs: editing code lives in the repo's config; campus names; unwatched connections stay
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+11
-11
@@ -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 | 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 | 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 | 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 | 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 | 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. |
|
| 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 | 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 | 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 | 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 | 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 | 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. |
|
| 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 | 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 | 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 | 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 | 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 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. |
|
| 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 | 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 | 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 | 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 | 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 | 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. |
|
| 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 | 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 | 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 | 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 | 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 | 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. |
|
| 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 | 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. |
|
| 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
|
## 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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)
|
## Still open (needs the user)
|
||||||
|
|
||||||
- Q1 Who gets the editing code besides you?
|
- Q1 Answered 2026-10-13: the editing code lives in config.json in the private repo; everyone with the repo has it.
|
||||||
- Q2 Live services at NA: only Sunday 06:30-13:30, or also midweek services or rehearsals that changes must wait for?
|
- 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 The other campuses: their names, and does each have a Mac that runs Companion (where the page and the watchdog would live)?
|
- Q3 Answered 2026-10-13: Apollo Beach, Woonsocket and Southcoast, each with a Mac Studio already running Companion.
|
||||||
- Q4 Unwatched Companion connections at NA (Govee x7, DiGiCo_OSC, m32, AbleSet, tcp-HyperDeck-1..6): leave them?
|
- Q4 Answered 2026-10-13: the unwatched connections stay unwatched.
|
||||||
|
|||||||
@@ -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.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.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 |
|
| `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 <date>", [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) |
|
| `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 |
|
| 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 |
|
| `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
|
## 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 <name> from <address>". 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 <name> from <address>". 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 <key> <fix> <identity> <time>` on the command variable (`sp_cmd`), exactly as a hold arrives, and the watchdog resolves the check from that key (deck address from the snapshot, MIDI address from the last-seen peer address, recording-deck guard, cooldown, one MIDI fix at a time), logs it as TEST, and refuses a `test` whose fix is attached to no check on that key, a netgear_poe / dante-auto / atem fix, a recording deck, or a bare `test <fix>`; it is offered only for the start/reconnect kinds (start-only Windows/Mac/helper/DVS builders, Reconnect a Companion connection, Switch a HyperDeck off and on, Reconnect a MIDI peer, Play the Spotify playlist) and never during a live service. "Check the Dante network now" sends the `resurvey` verb the same way. The page never writes a runtime file the watchdog also writes: `dante-map.json`, `live-now.json`, `fixes-running.json`, `started.json`, `state.json` and the command variable are the watchdog's alone.
|
- **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 <key> <fix> <identity> <time>` on the command variable (`sp_cmd`), exactly as a hold arrives, and the watchdog resolves the check from that key (deck address from the snapshot, MIDI address from the last-seen peer address, recording-deck guard, cooldown, one MIDI fix at a time), logs it as TEST, and refuses a `test` whose fix is attached to no check on that key, a netgear_poe / dante-auto / atem fix, a recording deck, or a bare `test <fix>`; it is offered only for the start/reconnect kinds (start-only Windows/Mac/helper/DVS builders, Reconnect a Companion connection, Switch a HyperDeck off and on, Reconnect a MIDI peer, Play the Spotify playlist) and never during a live service. "Check the Dante network now" sends the `resurvey` verb the same way. The page never writes a runtime file the watchdog also writes: `dante-map.json`, `live-now.json`, `fixes-running.json`, `started.json`, `state.json` and the command variable are the watchdog's alone.
|
||||||
- **Server surface.** `GET /api/logs/<name>` serves only names on its list; `/api/live/spotify?url=` fetches only `open.spotify.com`; ATEM uploads are capped at 20 MB, XML with a DOCTYPE or entity declarations is refused, and the file is parsed with the watchdog's own parser in the settings process; the Companion DB reader returns label, module id and `config.host` only (the module version only if the spike finds it trivially; no surfaces, never a raw row); every page export is fetched with `includeSecrets=false`.
|
- **Server surface.** `GET /api/logs/<name>` serves only names on its list; `/api/live/spotify?url=` fetches only `open.spotify.com`; ATEM uploads are capped at 20 MB, XML with a DOCTYPE or entity declarations is refused, and the file is parsed with the watchdog's own parser in the settings process; the Companion DB reader returns label, module id and `config.host` only (the module version only if the spike finds it trivially; no surfaces, never a raw row); every page export is fetched with `includeSecrets=false`.
|
||||||
- **Secrets:** presence only (keychain items exist + account, login key exists, public key text, alert web hook "set, N chars"). `alert_webhook` and any key named `*token`, `*secret`, `*password` are masked in every API response, diff, History meta and site bundle, and every file the settings agent writes in the data dir (`draft.json`, `history/*`, `machines-live.json`, `settings.log`, the `edit-key` fallback) is created 0600; the real value lives only in `config.json` and is re-attached on restore. The page never reads, shows or writes the Netgear password, never stores a credential for any computer or for git, and never runs `security add-generic-password` or makes a key for a person; it shows the exact line to type. The only `ssh-keygen` calls it makes are the confirmed `-R <ip>` (Forget the old login key) and the confirmed creation of the panel's own login key pair (Make the login key).
|
- **Secrets:** presence only (keychain items exist + account, login key exists, public key text, alert web hook "set, N chars"). `alert_webhook`, `site.editing_code` and any key named `*token`, `*secret`, `*password` are masked in every API response, diff, History meta and site bundle, and every file the settings agent writes in the data dir (`draft.json`, `history/*`, `machines-live.json`, `settings.log`) is created 0600; the real value lives only in `config.json` and is re-attached on restore. The page never reads, shows or writes the Netgear password, never stores a credential for any computer or for git, and never runs `security add-generic-password` or makes a key for a person; it shows the exact line to type. The only `ssh-keygen` calls it makes are the confirmed `-R <ip>` (Forget the old login key) and the confirmed creation of the panel's own login key pair (Make the login key).
|
||||||
- **Never automated:** setup scripts on other computers (ARD / PowerShell by the user, guideline 12), firewall or system changes, BIOS WoL, Remote Login, macOS permissions (Automation, Accessibility), creating Companion custom variables or triggers unless the Phase 1 tRPC spike proves it safe (then only with explicit confirmation and never a full import), module imports, `push_page --force`, full Companion imports, rebooting any device, power-cycling a PoE port or pushing the switcher file from a Run button (those fixes have no Run now), restarting the Resi bridge during a service ("never by itself"), any Apply or watchdog restart during a live service without the Emergency override, git pulls on the Ingest Mac.
|
- **Never automated:** setup scripts on other computers (ARD / PowerShell by the user, guideline 12), firewall or system changes, BIOS WoL, Remote Login, macOS permissions (Automation, Accessibility), creating Companion custom variables or triggers unless the Phase 1 tRPC spike proves it safe (then only with explicit confirmation and never a full import), module imports, `push_page --force`, full Companion imports, rebooting any device, power-cycling a PoE port or pushing the switcher file from a Run button (those fixes have no Run now), restarting the Resi bridge during a service ("never by itself"), any Apply or watchdog restart during a live service without the Emergency override, git pulls on the Ingest Mac.
|
||||||
- **Confirmations, one style:** a dialog that states the consequence and asks once; a typed word only for the Emergency override during a live service (`LIVE` + reason) and for Stop the watchdog (`STOP`: "the deck goes purple and every alarm is off until Start"). The dialogs: deleting a computer, key, fix or check shows everything that refers to it ("Delete Lighting PC and also remove 3 fixes, 4 checks, 1 restart key?"); "Run this fix now" names the computer or device; Restart the watchdog; Go back to this / Undo; a page-number change before the old page is cleared; a saved switcher file must be ticked after its diff is shown; Undo the page-number buttons on every page (rewrites 97 pages); Prune history (with the count); Re-import the pages now; Install the staged folder (shows the staged version); Forget the old login key (names the computer); Make the login key; Make a new editing code and changing the port (state the consequence); a Custom command edit asks for the editing code again.
|
- **Confirmations, one style:** a dialog that states the consequence and asks once; a typed word only for the Emergency override during a live service (`LIVE` + reason) and for Stop the watchdog (`STOP`: "the deck goes purple and every alarm is off until Start"). The dialogs: deleting a computer, key, fix or check shows everything that refers to it ("Delete Lighting PC and also remove 3 fixes, 4 checks, 1 restart key?"); "Run this fix now" names the computer or device; Restart the watchdog; Go back to this / Undo; a page-number change before the old page is cleared; a saved switcher file must be ticked after its diff is shown; Undo the page-number buttons on every page (rewrites 97 pages); Prune history (with the count); Re-import the pages now; Install the staged folder (shows the staged version); Forget the old login key (names the computer); Make the login key; changing the editing code or the port (state the consequence); a Custom command edit asks for the editing code again.
|
||||||
- **Watchdog restart safety:** the Engine keeps a registry of running fix threads (every thread started in `run_plan`, `run_force` and the test verb) and publishes it as `fixes-running.json`; its SIGTERM handler sets the stop event, refuses new fixes (hold, force, auto or test answer RESTARTING SOON on the key) and joins the registry for up to ExitTimeOut-5 s before exiting; `install.sh` writes the derived `ExitTimeOut` (max fix timeout + verify + 10, floor 60; today launchd's deadline is 5 s) into the watchdog plist before the handler ships; Apply waits until the registry is empty (60 s) before the kickstart; a HyperDeck off/on fix re-enables the connection in a `finally`, so a kill between its off and on never leaves a deck connection switched off; acknowledged keys, auto-fix tries and fix cooldowns persist in `state.json` keyed by (key, check identity) so a restart never re-runs a fix early or forgets an acknowledgement; the command variable is cleared once at start. There is no SIGHUP partial reload and no stop flag: every Apply is a full restart on the new config.
|
- **Watchdog restart safety:** the Engine keeps a registry of running fix threads (every thread started in `run_plan`, `run_force` and the test verb) and publishes it as `fixes-running.json`; its SIGTERM handler sets the stop event, refuses new fixes (hold, force, auto or test answer RESTARTING SOON on the key) and joins the registry for up to ExitTimeOut-5 s before exiting; `install.sh` writes the derived `ExitTimeOut` (max fix timeout + verify + 10, floor 60; today launchd's deadline is 5 s) into the watchdog plist before the handler ships; Apply waits until the registry is empty (60 s) before the kickstart; a HyperDeck off/on fix re-enables the connection in a `finally`, so a kill between its off and on never leaves a deck connection switched off; acknowledged keys, auto-fix tries and fix cooldowns persist in `state.json` keyed by (key, check identity) so a restart never re-runs a fix early or forgets an acknowledgement; the command variable is cleared once at start. There is no SIGHUP partial reload and no stop flag: every Apply is a full restart on the new config.
|
||||||
- **Guideline fit:** no per-key colour options in the schema; the legacy `service_only` flag sits under Advanced with a warning and has no effect while alarms are armed all day; the alarms window is not on the page at all (1); one schedule, labelled so only auto-fix and the change guard use it and alarms stay armed, the one sentence everywhere (2); the validator warns when a key has nothing to fix and no "also when green", and the HOLDING 2 s WILL line shows the answer (3); restart keys always have a fix and the watch list keeps them flashing (4); builders generate real, tested fixes with a guarded Run now (5, 6); debounce defaults stay 3/1/0 s (7); the gentle list is enforced by load_config and "never by itself" (8); page-number buttons re-run inside Apply whenever a page changes, and the Companion section counts "coloured on N of M pages" when opened, with [Colour them now] (9); the live-service guard uses the clock and the built-in recording signal, refuses rather than queues, leaves the live config untouched and has one logged override (10); no passwords (11); scripts only shown (12); the repo stays private (13); help texts short (14); Apply waits for proof (`started.json`, verified import) before saying done (15); History and the generated key guide replace most README tables (16).
|
- **Guideline fit:** no per-key colour options in the schema; the legacy `service_only` flag sits under Advanced with a warning and has no effect while alarms are armed all day; the alarms window is not on the page at all (1); one schedule, labelled so only auto-fix and the change guard use it and alarms stay armed, the one sentence everywhere (2); the validator warns when a key has nothing to fix and no "also when green", and the HOLDING 2 s WILL line shows the answer (3); restart keys always have a fix and the watch list keeps them flashing (4); builders generate real, tested fixes with a guarded Run now (5, 6); debounce defaults stay 3/1/0 s (7); the gentle list is enforced by load_config and "never by itself" (8); page-number buttons re-run inside Apply whenever a page changes, and the Companion section counts "coloured on N of M pages" when opened, with [Colour them now] (9); the live-service guard uses the clock and the built-in recording signal, refuses rather than queues, leaves the live config untouched and has one logged override (10); no passwords (11); scripts only shown (12); the repo stays private (13); help texts short (14); Apply waits for proof (`started.json`, verified import) before saying done (15); History and the generated key guide replace most README tables (16).
|
||||||
- The settings server imports only `load_config`, `CHECKS`, `RESTARTABLE`, `FIX_KINDS`, the ATEM parser and `make_page.build` from the watchdog code (never `run_once`), so a bug in the page cannot take the panel down; it runs as the GUI user with no sudo, as a separate process, so the page keeps working while the watchdog restarts or when the panel is purple.
|
- The settings server imports only `load_config`, `CHECKS`, `RESTARTABLE`, `FIX_KINDS`, the ATEM parser and `make_page.build` from the watchdog code (never `run_once`), so a bug in the page cannot take the panel down; it runs as the GUI user with no sudo, as a separate process, so the page keeps working while the watchdog restarts or when the panel is purple.
|
||||||
@@ -216,11 +216,11 @@ schema.py the registry: top-level keys, site/host/run/gear re
|
|||||||
validate(cfg, live=None) -> errors, warnings; plain_diff(a, b); mask_secrets(obj); phrases(cfg) (the
|
validate(cfg, live=None) -> errors, warnings; plain_diff(a, b); mask_secrets(obj); phrases(cfg) (the
|
||||||
"may run by itself ..." and window sentences built from the config)
|
"may run by itself ..." and window sentences built from the config)
|
||||||
settings/__init__.py adds the parent folder to sys.path so status_panel/make_page import from the data dir
|
settings/__init__.py adds the parent folder to sys.path so status_panel/make_page import from the data dir
|
||||||
settings/server.py stdlib ThreadingHTTPServer bound to IP addresses, not interface names: 127.0.0.1 always (the only listener
|
settings/server.py stdlib ThreadingHTTPServer bound to IP addresses, not interface names: 127.0.0.1 always (always)
|
||||||
that serves /edit-key) plus the IPv4 address of each LAN-use interface in hosts.ingest.networks resolved
|
plus the IPv4 address of each LAN-use interface in hosts.ingest.networks resolved
|
||||||
from ifconfig at start (every private-address interface when the list is empty), re-checked every minute
|
from ifconfig at start (every private-address interface when the list is empty), re-checked every minute
|
||||||
and rebound on change; one thread per address; editing code + Origin check on every write; reads open
|
and rebound on change; one thread per address; editing code + Origin check on every write; reads open
|
||||||
GET / /app.js /style.css /key-guide.html /cheat.html /where.html GET /edit-key (127.0.0.1 only)
|
GET / /app.js /style.css /key-guide.html /cheat.html /where.html
|
||||||
GET /api/schema /api/config (masked) /api/draft (ETag) PUT /api/draft (If-Match, 409 with the field) DELETE /api/draft
|
GET /api/schema /api/config (masked) /api/draft (ETag) PUT /api/draft (If-Match, 409 with the field) DELETE /api/draft
|
||||||
POST /api/validate /api/apply (lock; refused while live unless the body carries the Emergency word + reason)
|
POST /api/validate /api/apply (lock; refused while live unless the body carries the Emergency word + reason)
|
||||||
POST /api/restore/<id> /api/undo
|
POST /api/restore/<id> /api/undo
|
||||||
@@ -259,11 +259,11 @@ install.sh + --site / --config / --keep-config / --force-site-
|
|||||||
remote-access/*.template, companion-objects/, atem_state.js; writes both LaunchAgents (watchdog plist with the
|
remote-access/*.template, companion-objects/, atem_state.js; writes both LaunchAgents (watchdog plist with the
|
||||||
derived ExitTimeOut, settings plist with AbandonProcessGroup); restarts the watchdog first and waits for
|
derived ExitTimeOut, settings plist with AbandonProcessGroup); restarts the watchdog first and waits for
|
||||||
started.json, the settings agent last (detached); never overwrites site-owned files once the page exists;
|
started.json, the settings agent last (detached); never overwrites site-owned files once the page exists;
|
||||||
prints the /edit-key address; writes apply.log + a History entry
|
prints the page address; writes apply.log + a History entry
|
||||||
tests/test_schema.py test_wording.py test_apply.py test_probes.py test_settings.py (beside the existing tests/test_status_panel.py)
|
tests/test_schema.py test_wording.py test_apply.py test_probes.py test_settings.py (beside the existing tests/test_status_panel.py)
|
||||||
```
|
```
|
||||||
|
|
||||||
The page tools (`make_page.py`, `push_page.py`, `nav_keys.py`) run from and write to the **data dir** on the Ingest Mac (they load `HERE/config.json`; `nav_locations.json` and the `.companionconfig` outputs live there). The data dir holds, besides today's `config.json`, `atem-reference.xml`, `atem-reference.json`, `dante-map.json`, `nav_locations.json`, the two built pages, `status-panel.log`, `status-panel.err.log` and the `config.json.bak-*` files: `draft.json`, `history/` (+ `history/atem/<date>/`), `apply.log`, `apply.lock` (the server-side Apply lock: a second Apply is refused with "an Apply is already running"), `machines-live.json`, `settings.log`, `key-guide.html`, `build-hashes.json`, `fixes-running.json`, `live-now.json`, `started.json`, `state.json` (replaces `service-mode.json`, migrated once), `pages-out-of-sync.json`, `edit-key` (when the keychain is unavailable), and the deployed `remote-access/*.template` and `companion-objects/`. Every file the settings agent writes there (`draft.json`, `history/*`, `machines-live.json`, `settings.log`, the `edit-key` fallback) is created 0600.
|
The page tools (`make_page.py`, `push_page.py`, `nav_keys.py`) run from and write to the **data dir** on the Ingest Mac (they load `HERE/config.json`; `nav_locations.json` and the `.companionconfig` outputs live there). The data dir holds, besides today's `config.json`, `atem-reference.xml`, `atem-reference.json`, `dante-map.json`, `nav_locations.json`, the two built pages, `status-panel.log`, `status-panel.err.log` and the `config.json.bak-*` files: `draft.json`, `history/` (+ `history/atem/<date>/`), `apply.log`, `apply.lock` (the server-side Apply lock: a second Apply is refused with "an Apply is already running"), `machines-live.json`, `settings.log`, `key-guide.html`, `build-hashes.json`, `fixes-running.json`, `live-now.json`, `started.json`, `state.json` (replaces `service-mode.json`, migrated once), `pages-out-of-sync.json`, and the deployed `remote-access/*.template` and `companion-objects/`. Every file the settings agent writes there (`draft.json`, `history/*`, `machines-live.json`, `settings.log`) is created 0600.
|
||||||
|
|
||||||
**Code changes in the watchdog** (`status_panel.py`, additive, keeps its `.get(key, literal)` defaults): a `VERSION` constant, the "status panel running (code X)" line and `started.json` `{pid, started_at, config_sha}` at start; new keys read with defaults (`site.{id,name,ingest_name,ingest_hostname,ingest_ips,contacts,alert_prefix,live_services (defaults to auto_fix.windows; the migration writes it),dvs_helper,solo_dir,settings_port}`, `timing.{fix_cooldown_seconds,alert_after_seconds}`, `page_hold_ms`, `page_grid`, `page_stale_seconds`, `variables.flash`, `companion.{export_version,build,db}`, `auto_fix.{forget_seconds,keep_months}`, `dante_max_shared`, `atem.{compare,source_folder}`, `spotify.{playlist,name,open_wait,play_wait}`, `fixes.*.{never_auto,template,params}`, `checks.*.{template,params}`, `remote.shell`, `hosts.*.{name,os,role,notes,pingable,self,runs,networks,manual_done}`, `gear.*.{location,notes}`); overnight windows in `in_windows` (end <= start runs past midnight); the "HOLD 2s" and "WAIT 1 MIN" texts built from `page_hold_ms` / `fix_cooldown_seconds` (one `self.cooldown`); a fix-thread registry (started in `run_plan`, `run_force` and the test verb) that writes `fixes-running.json`; a SIGTERM handler (stop event, refuse new fixes with RESTARTING SOON, join the registry up to ExitTimeOut-5); the HyperDeck off/on fix re-enabling in a `finally`; `live-now.json` written on change / every 5 s with a timestamp from the hyperdeck checks that follow the recording (`status == record`); one atomic writer for `state.json` with acknowledgements, auto-fix tries and `fix_last` keyed by (key, check identity) per `IDENTITY`, dropped for a key whose checks changed, with `ServiceMode` merging into it (migrating `service-mode.json` once); the command variable cleared once at start (logged, a queued hold dropped); a `test <key> <fix> <identity> <unix>` verb in `handle_commands` that resolves the check from the key as `run_fixes` does (deck ip from the snapshot, MIDI address from the last-seen peer address, recording guard, cooldown) and refuses netgear_poe / dante-auto / atem / a fix not attached to that key / an identity not on the key; a `resurvey` verb; the Windows (`cmd`) variant of remote-check batching as `(cmd) >nul 2>&1 && echo SPn=0 || echo SPn=1` pairs joined with `&` (never a bare `%ERRORLEVEL%`), with cmd-batched checks refused for a PC whose DefaultShell is not cmd; wording from `site.*` and `schema.phrases` (log header, Dante log line, webhook text); fix kind `spotify` (playlist and name from `spotify.*`); `FIX_KINDS` exported and validated in `load_config`; the host validated for every fix, remote check and MIDI link (exists, has ssh, not self); `never_auto`; `EXPECTED_MODULE` per check type; `hyperdeck_probe` removed in P0 together with `TestHyperDeckProbe`/`FakeDeck` (dead since the HyperDeck hold toggles the connection); a python 3.9-3.12 self-check at start (telnetlib). No SIGHUP reload, no `then` chain, no stop flag, no commands folder poll.
|
**Code changes in the watchdog** (`status_panel.py`, additive, keeps its `.get(key, literal)` defaults): a `VERSION` constant, the "status panel running (code X)" line and `started.json` `{pid, started_at, config_sha}` at start; new keys read with defaults (`site.{id,name,ingest_name,ingest_hostname,ingest_ips,contacts,alert_prefix,live_services (defaults to auto_fix.windows; the migration writes it),dvs_helper,solo_dir,settings_port}`, `timing.{fix_cooldown_seconds,alert_after_seconds}`, `page_hold_ms`, `page_grid`, `page_stale_seconds`, `variables.flash`, `companion.{export_version,build,db}`, `auto_fix.{forget_seconds,keep_months}`, `dante_max_shared`, `atem.{compare,source_folder}`, `spotify.{playlist,name,open_wait,play_wait}`, `fixes.*.{never_auto,template,params}`, `checks.*.{template,params}`, `remote.shell`, `hosts.*.{name,os,role,notes,pingable,self,runs,networks,manual_done}`, `gear.*.{location,notes}`); overnight windows in `in_windows` (end <= start runs past midnight); the "HOLD 2s" and "WAIT 1 MIN" texts built from `page_hold_ms` / `fix_cooldown_seconds` (one `self.cooldown`); a fix-thread registry (started in `run_plan`, `run_force` and the test verb) that writes `fixes-running.json`; a SIGTERM handler (stop event, refuse new fixes with RESTARTING SOON, join the registry up to ExitTimeOut-5); the HyperDeck off/on fix re-enabling in a `finally`; `live-now.json` written on change / every 5 s with a timestamp from the hyperdeck checks that follow the recording (`status == record`); one atomic writer for `state.json` with acknowledgements, auto-fix tries and `fix_last` keyed by (key, check identity) per `IDENTITY`, dropped for a key whose checks changed, with `ServiceMode` merging into it (migrating `service-mode.json` once); the command variable cleared once at start (logged, a queued hold dropped); a `test <key> <fix> <identity> <unix>` verb in `handle_commands` that resolves the check from the key as `run_fixes` does (deck ip from the snapshot, MIDI address from the last-seen peer address, recording guard, cooldown) and refuses netgear_poe / dante-auto / atem / a fix not attached to that key / an identity not on the key; a `resurvey` verb; the Windows (`cmd`) variant of remote-check batching as `(cmd) >nul 2>&1 && echo SPn=0 || echo SPn=1` pairs joined with `&` (never a bare `%ERRORLEVEL%`), with cmd-batched checks refused for a PC whose DefaultShell is not cmd; wording from `site.*` and `schema.phrases` (log header, Dante log line, webhook text); fix kind `spotify` (playlist and name from `spotify.*`); `FIX_KINDS` exported and validated in `load_config`; the host validated for every fix, remote check and MIDI link (exists, has ssh, not self); `never_auto`; `EXPECTED_MODULE` per check type; `hyperdeck_probe` removed in P0 together with `TestHyperDeckProbe`/`FakeDeck` (dead since the HyperDeck hold toggles the connection); a python 3.9-3.12 self-check at start (telnetlib). No SIGHUP reload, no `then` chain, no stop flag, no commands folder poll.
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ The source's test 10 (`test_apply_class`) went with the apply classes and is not
|
|||||||
12. `tests/test_wording.py`: scans `settings/ui/strings.json`, every schema help and label, and the Help pages (not the rendered DOM) for the forbidden-token list (underscored words, `--flags`, `.py/.json/.plist`, `sp_state`-style names, launchd labels, `r2c6`-style positions, bare connection labels outside a pill, "tile", "machine", "host", "builder", "edit key", "nav key", "Sunday", "North Attleboro", "always on", "detect", "Refresh") outside Advanced sections and generated text; the four chosen words (key, computer, fix, connection) and "editing code", "login key", "page-number buttons" are allowed; every button label starts with a listed verb and read-only ones with "Check"; the alarms sentence appears verbatim wherever alarms are mentioned.
|
12. `tests/test_wording.py`: scans `settings/ui/strings.json`, every schema help and label, and the Help pages (not the rendered DOM) for the forbidden-token list (underscored words, `--flags`, `.py/.json/.plist`, `sp_state`-style names, launchd labels, `r2c6`-style positions, bare connection labels outside a pill, "tile", "machine", "host", "builder", "edit key", "nav key", "Sunday", "North Attleboro", "always on", "detect", "Refresh") outside Advanced sections and generated text; the four chosen words (key, computer, fix, connection) and "editing code", "login key", "page-number buttons" are allowed; every button label starts with a listed verb and read-only ones with "Check"; the alarms sentence appears verbatim wherever alarms are mentioned.
|
||||||
13. `test_push_page_markers`: `TestPage.test_push_page_looks_at_what_is_on_the_page` extended with a page holding only our marked tiles plus the two jump keys (accepted), a page with only nav keys (accepted), a page with the expected name and one foreign key (refused, with the key's position in the reason), and one with a foreign key and another name (refused).
|
13. `test_push_page_markers`: `TestPage.test_push_page_looks_at_what_is_on_the_page` extended with a page holding only our marked tiles plus the two jump keys (accepted), a page with only nav keys (accepted), a page with the expected name and one foreign key (refused, with the key's position in the reason), and one with a foreign key and another name (refused).
|
||||||
14. `test_test_verb`: in `TestFixes` with the stubbed executor, `test HD2 hyperdeck <identity> <now>` resolves HyperDeck_2's conn and ip, `test MIDI midi_reconnect {"host":"tracks","session":"NA Tracks","peer":"Studio B"} <now>` resolves exactly that peer's address and leaves the other five alone, a recording deck is left alone, a bare `test <fix>` is refused, an identity not on the key is refused.
|
14. `test_test_verb`: in `TestFixes` with the stubbed executor, `test HD2 hyperdeck <identity> <now>` resolves HyperDeck_2's conn and ip, `test MIDI midi_reconnect {"host":"tracks","session":"NA Tracks","peer":"Studio B"} <now>` resolves exactly that peer's address and leaves the other five alone, a recording deck is left alone, a bare `test <fix>` is refused, an identity not on the key is refused.
|
||||||
15. `test_settings`: routes against the fake Companion: every read answers without the code (logs, history bodies, masked config, inventories, generated scripts) and every write is refused without it; `/edit-key` 404 on a LAN socket and 200 on loopback (the only listener that serves it); the bind set resolved to IPv4 addresses from a canned `ifconfig` with a full / empty / stale `networks` list, 127.0.0.1 always present, and a changed address rebound on the minute re-check; lockout after 5 wrong codes; 409 field-wise merge and the same-field message; a Custom command edit needs the editing code typed again; secrets masked in every response; every file the settings agent writes in the data dir (`draft.json`, `history/*`, `machines-live.json`, `settings.log`, the `edit-key` fallback) is created 0600; the Origin check refuses a write from another host.
|
15. `test_settings`: routes against the fake Companion: every read answers without the code (logs, history bodies, masked config, inventories, generated scripts) and every write is refused without it; the code is read from `site.editing_code` and masked in `/api/config`; the bind set resolved to IPv4 addresses from a canned `ifconfig` with a full / empty / stale `networks` list, 127.0.0.1 always present, and a changed address rebound on the minute re-check; lockout after 5 wrong codes; 409 field-wise merge and the same-field message; a Custom command edit needs the editing code typed again; secrets masked in every response; every file the settings agent writes in the data dir (`draft.json`, `history/*`, `machines-live.json`, `settings.log`) is created 0600; the Origin check refuses a write from another host.
|
||||||
16. Existing tests: the behaviour suite loads `tests/fixture-config.json`; `TestPage`/`TestFixes` use `find_tile(cfg, id)`; `TestHyperDeckProbe`/`FakeDeck` deleted with `hyperdeck_probe` in P0 (dead since the HyperDeck hold toggles the connection); the valid-fix-kinds assertion reads `status_panel.FIX_KINDS`; the committed-pages test points at `sites/na` after Phase 6.
|
16. Existing tests: the behaviour suite loads `tests/fixture-config.json`; `TestPage`/`TestFixes` use `find_tile(cfg, id)`; `TestHyperDeckProbe`/`FakeDeck` deleted with `hyperdeck_probe` in P0 (dead since the HyperDeck hold toggles the connection); the valid-fix-kinds assertion reads `status_panel.FIX_KINDS`; the committed-pages test points at `sites/na` after Phase 6.
|
||||||
17. Phase 5: `test_generate_from_data_dir`: install.sh's copy step into a temp data dir, then generate.py finds every template and fills it.
|
17. Phase 5: `test_generate_from_data_dir`: install.sh's copy step into a temp data dir, then generate.py finds every template and fills it.
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ Every inventory item, and where the plan handles it or why it is deliberately no
|
|||||||
| coremidi NETWORK_DRIVERS, client names, identity rules | DNE; "no network MIDI driver found" light on the Ingest card and in the MIDI pickers; typed fallback when a Mac cannot be read |
|
| coremidi NETWORK_DRIVERS, client names, identity rules | DNE; "no network MIDI driver found" light on the Ingest card and in the MIDI pickers; typed fallback when a Mac cannot be read |
|
||||||
| make_page EXPORT_VERSION/GRID/STALE/HOLD_MS/stale texts/style/jump keys/NAV_LOCATIONS/nav_templates/hold decision/notes/output names/action ids | editable: stamps, grid, stale, hold, jump chips; hold decision = built-page hash detection with verified import (§C, active from Phase 2 as a refusal); jump keys carry markers; nav template per version from companion-objects; outputs to the data dir; the rest DNE |
|
| make_page EXPORT_VERSION/GRID/STALE/HOLD_MS/stale texts/style/jump keys/NAV_LOCATIONS/nav_templates/hold decision/notes/output names/action ids | editable: stamps, grid, stale, hold, jump chips; hold decision = built-page hash detection with verified import (§C, active from Phase 2 as a refusal); jump keys carry markers; nav template per version from companion-objects; outputs to the data dir; the rest DNE |
|
||||||
| push_page defaults/import mechanics/safety rule; nav_keys defaults/walk/nav_locations.json | defaults from the config; marker-only `MARKERS` rule in the Apply path (name shortcut removed), foreign keys listed by position, refusal surfaced, never --force; export walk with the 5-miss end; "coloured on N of M pages" counted when the Companion section is opened (no periodic probe), [Colour them now] and [Undo]; per site, data dir |
|
| push_page defaults/import mechanics/safety rule; nav_keys defaults/walk/nav_locations.json | defaults from the config; marker-only `MARKERS` rule in the Apply path (name shortcut removed), foreign keys listed by position, refusal surfaced, never --force; export walk with the 5-miss end; "coloured on N of M pages" counted when the Companion section is opened (no periodic probe), [Colour them now] and [Undo]; per site, data dir |
|
||||||
| install.sh label/DEST/file list/plist/deploy path/page workflow | updated (§E): derived ExitTimeOut, AbandonProcessGroup, pre-validation, prev/ + --rollback with VERSION, watchdog first + started.json wait, settings agent last, one-shot launchd install job; Apply replaces the page workflow; `--site/--config/--keep-config/--force-site-files`; site-owned files never overwritten; the editing code made by the settings agent; phase rule (§C.7) |
|
| install.sh label/DEST/file list/plist/deploy path/page workflow | updated (§E): derived ExitTimeOut, AbandonProcessGroup, pre-validation, prev/ + --rollback with VERSION, watchdog first + started.json wait, settings agent last, one-shot launchd install job; Apply replaces the page workflow; `--site/--config/--keep-config/--force-site-files`; site-owned files never overwritten; the editing code read from the config; phase rule (§C.7) |
|
||||||
| NA Ingest key, Automation permission, keychain, DVS helper on Ingest, Companion app checklist, connection labels | Ingest card (§B) + Site setup > Companion; one Automation grant (watchdog); Make the login key |
|
| NA Ingest key, Automation permission, keychain, DVS helper on Ingest, Companion app checklist, connection labels | Ingest card (§B) + Site setup > Companion; one Automation grant (watchdog); Make the login key |
|
||||||
| windows-setup.ps1 $Key/$IngestIPs/app list/task names/elevation/sshd/firewall/rtpMIDI/WoL/DefaultShell/manual steps/RESULT | template from `site.ingest_ips` + `runs` (§B), regex-checked (no `!`/`^`), deployed to the data dir; RESULT paste; elevation logic unchanged; DefaultShell set to cmd.exe (the user OKs); Get-CimInstance probes; by hand on the PC |
|
| windows-setup.ps1 $Key/$IngestIPs/app list/task names/elevation/sshd/firewall/rtpMIDI/WoL/DefaultShell/manual steps/RESULT | template from `site.ingest_ips` + `runs` (§B), regex-checked (no `!`/`^`), deployed to the data dir; RESULT paste; elevation logic unchanged; DefaultShell set to cmd.exe (the user OKs); Get-CimInstance probes; by hand on the PC |
|
||||||
| mac-authorize.sh key, mac-dvs-helper.sh, install-solo-mode.sh, solo-mode-agent.swift paths/menu titles/bundle id/Accessibility | templates in the data dir incl. the swift template with `{{SOLO_DIR}}`, `{{PT_BUNDLE_ID}}`, `{{SOLO_MENU_PATH}}`; Accessibility light and re-grant note; test 17; by hand on that Mac |
|
| mac-authorize.sh key, mac-dvs-helper.sh, install-solo-mode.sh, solo-mode-agent.swift paths/menu titles/bundle id/Accessibility | templates in the data dir incl. the swift template with `{{SOLO_DIR}}`, `{{PT_BUNDLE_ID}}`, `{{SOLO_MENU_PATH}}`; Accessibility light and re-grant note; test 17; by hand on that Mac |
|
||||||
@@ -395,7 +395,7 @@ Every inventory item, and where the plan handles it or why it is deliberately no
|
|||||||
| LiveStream PC: host, WoL MAC/broadcast, sshd/firewall/keys, Start OBS elevated, OBS streaming variable | card; WoL light; BIOS tick; the OBS streaming variable is not a live signal in v1 (only the built-in recording-deck signal); "OBS is streaming" is added as a built-in kind only if the Phase 1 spike finds the module variable and its on/off values |
|
| LiveStream PC: host, WoL MAC/broadcast, sshd/firewall/keys, Start OBS elevated, OBS streaming variable | card; WoL light; BIOS tick; the OBS streaming variable is not a live signal in v1 (only the built-in recording-deck signal); "OBS is streaming" is added as a built-in kind only if the Phase 1 spike finds the module variable and its on/off values |
|
||||||
| Bible TV PC: host (Wi-Fi, blocks ping), sshd/firewall/keys, Start BibleApp at logon, Wi-Fi power settings | card; `pingable:false`; Wi-Fi tick + note; the page is not served on that Wi-Fi network once the LAN port is named |
|
| Bible TV PC: host (Wi-Fi, blocks ping), sshd/firewall/keys, Start BibleApp at logon, Wi-Fi power settings | card; `pingable:false`; Wi-Fi tick + note; the page is not served on that Wi-Fi network once the LAN port is named |
|
||||||
| devices: ATEM, HyperDecks, LED, Tascam, X32, Shure, SuperRack, Resi decoder/encoder/cloud, gateway, Netgear switches, Studio B PoE boxes | the device list under each Map row and the **Gear** filter (one row each: address, kind, where it is, keys that watch it, light) linked from key editors, the Dante map, the ATEM section and the Help card; recording decks feed the live-service guard; the Resi encoder's HTTP answer is deliberately not a live signal (a Resi live kind is offered only if the spike finds a module variable) |
|
| devices: ATEM, HyperDecks, LED, Tascam, X32, Shure, SuperRack, Resi decoder/encoder/cloud, gateway, Netgear switches, Studio B PoE boxes | the device list under each Map row and the **Gear** filter (one row each: address, kind, where it is, keys that watch it, light) linked from key editors, the Dante map, the ATEM section and the Help card; recording decks feed the live-service guard; the Resi encoder's HTTP answer is deliberately not a live signal (a Resi live kind is offered only if the spike finds a module variable) |
|
||||||
| secrets & permissions: ssh key, known_hosts (stale host key: Forget button), Netgear login, sudoers x4, Accessibility, Automation (watchdog only), Remote Login (incl. Ingest for the MacBook alias), ARD, Windows admin/execution policy, MacBook keychain, alert_webhook, editing code | lights (§B); §D never stored, masked everywhere incl. every data-dir copy (0600), re-attached on restore; by hand on each computer; web hook masked on Site; the one code made in the GUI session and shown only on loopback; lockout; command edits ask for the code again |
|
| secrets & permissions: ssh key, known_hosts (stale host key: Forget button), Netgear login, sudoers x4, Accessibility, Automation (watchdog only), Remote Login (incl. Ingest for the MacBook alias), ARD, Windows admin/execution policy, MacBook keychain, alert_webhook, editing code | lights (§B); §D never stored, masked everywhere incl. every data-dir copy (0600), re-attached on restore; by hand on each computer; web hook masked on Site; the one code kept in the repo's config and masked in responses; lockout; command edits ask for the code again |
|
||||||
| workflow: deploy, rebuild/import pages, nav keys, offline tests, --once, new ATEM file, Companion one-time setup | §C Apply; no preview run: the key editor's WHAT THIS KEY WILL SAY line shows what a key will say and validate builds both pages in memory; ATEM upload / pick-newest with diff; Companion lights; Install the staged folder (one-shot launchd job, validated, rollback-able, survives the agent restart) |
|
| workflow: deploy, rebuild/import pages, nav keys, offline tests, --once, new ATEM file, Companion one-time setup | §C Apply; no preview run: the key editor's WHAT THIS KEY WILL SAY line shows what a key will say and validate builds both pages in memory; ATEM upload / pick-newest with diff; Companion lights; Install the staged folder (one-shot launchd job, validated, rollback-able, survives the agent restart) |
|
||||||
|
|
||||||
**Lens 4: operator workflow and Companion-side state**
|
**Lens 4: operator workflow and Companion-side state**
|
||||||
|
|||||||
+12
-12
@@ -14,7 +14,7 @@ Four words are used everywhere on it: a **key** (on the Stream Deck), a **comput
|
|||||||
|
|
||||||
- It runs on the Ingest Mac as a second background program beside the watchdog, installed by the same `install.sh`. Plain Python, nothing to install. It is a separate program so a bug in the page can never stop the panel, and the page keeps working while the watchdog restarts.
|
- It runs on the Ingest Mac as a second background program beside the watchdog, installed by the same `install.sh`. Plain Python, nothing to install. It is a separate program so a bug in the page can never stop the panel, and the page keeps working while the watchdog restarts.
|
||||||
- Open **http://na-ingest.local:8020/** from any Mac, PC, iPad or phone on the church network. The address is printed on the cheat sheet and written into the ALL key's note in Companion, so anyone who finds the deck finds the page.
|
- Open **http://na-ingest.local:8020/** from any Mac, PC, iPad or phone on the church network. The address is printed on the cheat sheet and written into the ALL key's note in Companion, so anyone who finds the deck finds the page.
|
||||||
- **Anyone on the network can look** at everything: the deck, the computers, the logs, the history, the help. **Changing anything needs the editing code**: switch **Editing** on in the top bar, type the code once and your first name; the browser remembers both. The code is a few random words the page makes itself on first start; it is shown only on the Ingest Mac's own screen (at http://127.0.0.1:8020/edit-key), never over the network. It is not a password for anything else. Five wrong tries lock the browser out for ten minutes.
|
- **Anyone on the network can look** at everything: the deck, the computers, the logs, the history, the help. **Changing anything needs the editing code**: switch **Editing** on in the top bar, type the code once and your first name; the browser remembers both. The code is a setting in the campus's `config.json`, which lives in the private git repo, so everyone who has the repo has it; change it on Site setup › Site. The page never shows it to anyone who is not already editing. It is not a password for anything else. Five wrong tries lock the browser out for ten minutes.
|
||||||
- It edits the live settings file the watchdog reads (in `~/Library/Application Support/status-panel/`). From then on the Ingest Mac's copy is the truth for that campus; the git repo keeps a snapshot you export from the page.
|
- It edits the live settings file the watchdog reads (in `~/Library/Application Support/status-panel/`). From then on the Ingest Mac's copy is the truth for that campus; the git repo keeps a snapshot you export from the page.
|
||||||
|
|
||||||
## 3. What you see
|
## 3. What you see
|
||||||
@@ -120,7 +120,7 @@ COMPUTERS - MAP [Check computers now] looked 2
|
|||||||
|
|
||||||
Six sections, each opening on the few things a person changes:
|
Six sections, each opening on the few things a person changes:
|
||||||
|
|
||||||
- **Site:** site name, this Mac's name and addresses, contacts for the cheat sheet, the alert message hook. Advanced: page port, the login key (present / not, public half to copy), helper paths, make a new editing code, install the staged folder, export the site bundle.
|
- **Site:** site name, this Mac's name and addresses, contacts for the cheat sheet, the alert message hook. Advanced: page port, the login key (present / not, public half to copy), helper paths, the editing code, install the staged folder, export the site bundle.
|
||||||
- **Schedules:** one schedule, the **live services** (Sunday 06:30–13:30 today): during them changes wait, auto-fix may act, alarms are always on. The tick list of fixes auto-fix may run. Advanced: the timing numbers (a fault must last 3 s, flapping, the 60 s cooldown, the 2 s hold).
|
- **Schedules:** one schedule, the **live services** (Sunday 06:30–13:30 today): during them changes wait, auto-fix may act, alarms are always on. The tick list of fixes auto-fix may run. Advanced: the timing numbers (a fault must last 3 s, flapping, the 60 s cooldown, the 2 s hold).
|
||||||
- **Companion:** the lights (variables exist, blink trigger, our pages exist, quiet Tascam module, ATEM module), the live list of connections with their module and which key watches each, "Add a key for it" for the ones nothing watches, the page-number buttons ("coloured on 96 of 97 pages" + Colour them now / Undo). Advanced: address, variable names, nav settings, grid, purple-after, export stamps.
|
- **Companion:** the lights (variables exist, blink trigger, our pages exist, quiet Tascam module, ATEM module), the live list of connections with their module and which key watches each, "Add a key for it" for the ones nothing watches, the page-number buttons ("coloured on 96 of 97 pages" + Colour them now / Undo). Advanced: address, variable names, nav settings, grid, purple-after, export stamps.
|
||||||
- **Network & Dante:** the port and gateway the NET key watches, the Dante switches, and the **Dante device list** (the one place the DANTE key's devices are edited, with key text and per-device fix), "Check the Dante network now". Advanced: the blip rule, survey interval.
|
- **Network & Dante:** the port and gateway the NET key watches, the Dante switches, and the **Dante device list** (the one place the DANTE key's devices are edited, with key text and per-device fix), "Check the Dante network now". Advanced: the blip rule, survey interval.
|
||||||
@@ -160,9 +160,9 @@ The Setup tab generates each computer's one-time script (the Mac authorise scrip
|
|||||||
|
|
||||||
## 6. The other campuses
|
## 6. The other campuses
|
||||||
|
|
||||||
One settings file per campus = one site block plus that campus's computers, keys, checks, fixes, windows and gear. The code never contains a site name, an address, a label, a playlist, a path or a day name; everything site-shaped is data. Differences between campuses are plain differences in that data: fewer HD keys, no Studio B, another playlist, other Dante names, an extra PC, other page numbers, a Saturday evening service that runs past midnight.
|
Apollo Beach, Woonsocket and Southcoast each already have a Mac Studio running Companion; the page and the watchdog go on that Mac. One settings file per campus = one site block plus that campus's computers, keys, checks, fixes, windows and gear. The code never contains a site name, an address, a label, a playlist, a path or a day name; everything site-shaped is data. Differences between campuses are plain differences in that data: fewer HD keys, no Studio B, another playlist, other Dante names, an extra PC, other page numbers, a Saturday evening service that runs past midnight.
|
||||||
|
|
||||||
Setting up a new campus: install Companion and the command-line tools on its Ingest Mac, copy the folder over, run `install.sh --site <name>` once (it uses the repo's `sites/<name>/` if there is one, otherwise the empty template), read the editing code off that screen, do the Companion one-time steps (section 12). Then on the page: **New campus from this one** starts from a site bundle (export NA's from its page and copy it over with the folder, or pick any `sites/` folder in the copied repo) and walks you through it — keep, rename or drop each computer (dropping one drops every key, check and fix that used it and lists them), map the connection labels, Dante devices and MIDI sessions from that Companion's live lists, set the playlist, switcher, network and schedule, Review & Apply. Everything copied from NA is flagged "copied from North Attleboro — check it" until touched. The repo keeps one folder per site (`sites/na/`, `sites/<new>/`); the page's **Export site bundle** gives you the zip to drop in.
|
Setting up a new campus: install Companion and the command-line tools on its Ingest Mac, copy the folder over, run `install.sh --site <name>` once (it uses the repo's `sites/<name>/` if there is one, otherwise the empty template), do the Companion one-time steps (section 12). Then on the page: **New campus from this one** starts from a site bundle (export NA's from its page and copy it over with the folder, or pick any `sites/` folder in the copied repo) and walks you through it — keep, rename or drop each computer (dropping one drops every key, check and fix that used it and lists them), map the connection labels, Dante devices and MIDI sessions from that Companion's live lists, set the playlist, switcher, network and schedule, Review & Apply. Everything copied from NA is flagged "copied from North Attleboro — check it" until touched. The repo keeps one folder per site (`sites/na/`, `sites/<new>/`); the page's **Export site bundle** gives you the zip to drop in.
|
||||||
|
|
||||||
## 7. Saving a change
|
## 7. Saving a change
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ Setting up a new campus: install Companion and the command-line tools on its Ing
|
|||||||
## 8. Safety
|
## 8. Safety
|
||||||
|
|
||||||
- The page answers only on the church network; everything is readable, nothing changes without the editing code; every change is logged with name and address.
|
- The page answers only on the church network; everything is readable, nothing changes without the editing code; every change is logged with name and address.
|
||||||
- No passwords are stored or typed: the login key stays a file on the Ingest Mac; the Netgear login stays in the keychain (the page shows the one line to type in Terminal); the alert hook is masked everywhere; the editing code is made by the page and shown only on the Ingest screen.
|
- No passwords are stored or typed: the login key stays a file on the Ingest Mac; the Netgear login stays in the keychain (the page shows the one line to type in Terminal); the alert hook is masked everywhere; the editing code is a setting in the repo's config (whoever has the repo has it) and is masked for anyone who is not editing.
|
||||||
- The page never runs a command before Apply. Fixes and checks are built from fields and run only by the watchdog, which applies its usual guards (cooldown, a recording deck is left alone, one MIDI fix at a time). The only raw command field is under Advanced, shown in red, and asks for the code again.
|
- The page never runs a command before Apply. Fixes and checks are built from fields and run only by the watchdog, which applies its usual guards (cooldown, a recording deck is left alone, one MIDI fix at a time). The only raw command field is under Advanced, shown in red, and asks for the code again.
|
||||||
- Never automated: the setup scripts on other computers, firewall or system changes, BIOS, macOS permission prompts, creating Companion's custom variables (unless a safe way is proven first), rebooting a device, power-cycling a PoE port or pushing a switcher file from a Run button, restarting the Resi bridge by itself, anything during a live service without the Emergency button.
|
- Never automated: the setup scripts on other computers, firewall or system changes, BIOS, macOS permission prompts, creating Companion's custom variables (unless a safe way is proven first), rebooting a device, power-cycling a PoE port or pushing a switcher file from a Run button, restarting the Resi bridge by itself, anything during a live service without the Emergency button.
|
||||||
- Deleting a computer, key, fix or check shows everything that refers to it and asks. One confirmation style everywhere: a dialog that states the consequence; a typed word only for Emergency and for Stop the watchdog.
|
- Deleting a computer, key, fix or check shows everything that refers to it and asks. One confirmation style everywhere: a dialog that states the consequence; a typed word only for Emergency and for Stop the watchdog.
|
||||||
@@ -202,7 +202,7 @@ Each step is deployed on its own, outside a service, with tests green, a live ch
|
|||||||
| Step | What you get | What you can try | Size |
|
| Step | What you get | What you can try | Size |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 0 Groundwork | The registry and its tests; the watchdog's start stamp, safe restart, remembered acks, "run this fix" verb; install.sh changes. No page yet. | Nothing changes on the deck. Hold SPOTIFY still works. | 1.5 days |
|
| 0 Groundwork | The registry and its tests; the watchdog's start stamp, safe restart, remembered acks, "run this fix" verb; install.sh changes. No page yet. | Nothing changes on the deck. Hold SPOTIFY still works. | 1.5 days |
|
||||||
| 1 The page, read-only | Overview with the live deck, Keys viewer, Computers Map/cards/Gear with live looks, Logs, History list, Help with the key guide and cheat sheet. The editing code exists. Also the small Companion experiments (can it create variables safely, which OBS variable says "streaming"). | Open it from your phone; see every computer and what runs on it; print the cheat sheet; a newcomer reads it back to you. | 3 days |
|
| 1 The page, read-only | Overview with the live deck, Keys viewer, Computers Map/cards/Gear with live looks, Logs, History list, Help with the key guide and cheat sheet. Also the small Companion experiments (can it create variables safely, which OBS variable says "streaming"). | Open it from your phone; see every computer and what runs on it; print the cheat sheet; a newcomer reads it back to you. | 3 days |
|
||||||
| 2 Editing + Apply | Drafts, Review, Apply with rollback and the live-service guard; Site setup editors; key editor with check and fix builders; Run this fix now. Moving keys is refused until step 3. | Change HD2 "may have no disk" and Apply; paste a new playlist link; add a Dante device; Go back to yesterday; try an Apply during a pretend service and see it refused. | 4 days |
|
| 2 Editing + Apply | Drafts, Review, Apply with rollback and the live-service guard; Site setup editors; key editor with check and fix builders; Run this fix now. Moving keys is refused until step 3. | Change HD2 "may have no disk" and Apply; paste a new playlist link; add a Dante device; Go back to yesterday; try an Apply during a pretend service and see it refused. | 4 days |
|
||||||
| 3 Layout | Drag keys, add/delete keys, the Restart page, page numbers; Apply re-imports pages and recolours page-number buttons. | Move LED, add HD7, delete HD5, Apply, watch the deck change. | 2 days |
|
| 3 Layout | Drag keys, add/delete keys, the Restart page, page numbers; Apply re-imports pages and recolours page-number buttons. | Move LED, add HD7, delete HD5, Apply, watch the deck change. | 2 days |
|
||||||
| 4 Jobs | Change the playlist, Add or remove a HyperDeck, Add a MIDI link, Add a computer, Make a PC program startable; "Add a key for it" on unwatched connections. | Untick HD1/3/5/6 and run on 2 and 4; add a MIDI link to a Mac that is off. | 2.5 days |
|
| 4 Jobs | Change the playlist, Add or remove a HyperDeck, Add a MIDI link, Add a computer, Make a PC program startable; "Add a key for it" on unwatched connections. | Untick HD1/3/5/6 and run on 2 and 4; add a MIDI link to a Mac that is off. | 2.5 days |
|
||||||
@@ -213,7 +213,7 @@ Later, if wanted: a Training copy of the page where nothing is real; an "OBS is
|
|||||||
|
|
||||||
## 12. What you still do by hand
|
## 12. What you still do by hand
|
||||||
|
|
||||||
- **A new Ingest Mac:** install Companion, `xcode-select --install`, Remote Login on, copy the folder over, run `install.sh --site <name>` once, read the editing code off its screen.
|
- **A new Ingest Mac:** install Companion, `xcode-select --install`, Remote Login on, copy the folder over, run `install.sh --site <name>` once.
|
||||||
- **Companion, per campus:** create the custom variables `sp_state`, `sp_hb`, `sp_cmd`, `flash_tick` and the blink trigger (the page gives you the file to import, or a button if step 1 proves it safe); add pages up to the Status and Restart numbers; install the ATEM module; import the quiet Tascam module where there is a DA-6400; name the Stream Decks. Why: Companion only allows these in its own UI; a full import would wipe the variables.
|
- **Companion, per campus:** create the custom variables `sp_state`, `sp_hb`, `sp_cmd`, `flash_tick` and the blink trigger (the page gives you the file to import, or a button if step 1 proves it safe); add pages up to the Status and Restart numbers; install the ATEM module; import the quiet Tascam module where there is a DA-6400; name the Stream Decks. Why: Companion only allows these in its own UI; a full import would wipe the variables.
|
||||||
- **Every other Mac:** Remote Login on; paste the generated authorise script (and the DVS helper where DVS runs) via ARD "Send UNIX Command as root"; make the Audio MIDI Setup sessions the page names; on Studio B run the generated solo-mode install and allow Accessibility on that screen. Why: root rights and permission prompts must come from a person at that Mac.
|
- **Every other Mac:** Remote Login on; paste the generated authorise script (and the DVS helper where DVS runs) via ARD "Send UNIX Command as root"; make the Audio MIDI Setup sessions the page names; on Studio B run the generated solo-mode install and allow Accessibility on that screen. Why: root rights and permission prompts must come from a person at that Mac.
|
||||||
- **Every Windows PC:** run the generated setup script as administrator, paste its RESULT block into the page; BIOS wake-on-LAN; keep the Lighting PC on Public; Vista autoload; the Bible TV Wi-Fi power settings (or Ethernet). Why: system and firewall changes need your OK; BIOS has no remote path.
|
- **Every Windows PC:** run the generated setup script as administrator, paste its RESULT block into the page; BIOS wake-on-LAN; keep the Lighting PC on Public; Vista autoload; the Bible TV Wi-Fi power settings (or Ethernet). Why: system and firewall changes need your OK; BIOS has no remote path.
|
||||||
@@ -221,12 +221,12 @@ Later, if wanted: a Training copy of the page where nothing is real; an "OBS is
|
|||||||
- **ATEM saved file:** save it from ATEM Software Control when the show changes, then "Pick the newest file" on the ATEM section, outside a service.
|
- **ATEM saved file:** save it from ATEM Software Control when the show changes, then "Pick the newest file" on the ATEM section, outside a service.
|
||||||
- **Code updates (you):** `scp` the folder to the Ingest Mac as today, then Site setup → Install the staged folder. Drop the exported site bundle into `sites/<name>/` when the repo should hold the latest snapshot. The repo stays private.
|
- **Code updates (you):** `scp` the folder to the Ingest Mac as today, then Site setup → Install the staged folder. Drop the exported site bundle into `sites/<name>/` when the repo should hold the latest snapshot. The repo stays private.
|
||||||
|
|
||||||
## 13. Questions for you (none of these block steps 0–2)
|
## 13. Answers so far
|
||||||
|
|
||||||
1. Who gets the editing code besides you?
|
1. **Who can edit:** the editing code lives in `config.json` in the private repo, so everyone with the repo has it (answered 2026-10-13).
|
||||||
2. Live services at NA: only Sunday 6:30–13:30, or also midweek services or rehearsals that changes should wait for?
|
2. **When changes must wait:** the page refuses to apply a change while a service is on, so nothing is restarted mid-service. Today it knows Sunday 6:30 AM–1:30 PM (and any time a HyperDeck is recording). Still open: are there other times — a midweek service, a rehearsal — when it should refuse too? If not, Sunday is the only window.
|
||||||
3. The other campuses: names, and does each have a Mac that runs Companion? (The page and the watchdog live on that Mac.)
|
3. **The other campuses:** Apollo Beach, Woonsocket and Southcoast, each with a Mac Studio already running Companion (answered 2026-10-13).
|
||||||
4. The Companion connections nothing watches today (7 Govee lights, DiGiCo_OSC, m32, AbleSet, tcp-HyperDeck-1..6): leave them unwatched?
|
4. **Unwatched Companion connections** (7 Govee lights, DiGiCo_OSC, m32, AbleSet, tcp-HyperDeck-1..6): stay unwatched (answered 2026-10-13).
|
||||||
|
|
||||||
## 14. How this plan was audited
|
## 14. How this plan was audited
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user