Files
UmbraAudio/infra/umbra-node.json
B.D. Murphy 3d69187449 Answer the controller's data request, and audit what we send
The UmbraTrans controller asked (commit 70c23fd) for machine-readable metadata
about the audio system, in a specified layout, with a validator that ships with
the request so the sender can check its own work. That request is answered here.

scripts/build_controller_bundle.py emits that bundle: one real book (Deal
Hunter), 8,884 records, no media, no credentials, no hostnames. It passes the
controller's own validator, exit 0.

Three things in this system will break a mapping that does not know them, and
the bundle's real purpose is to say so precisely rather than to look tidy:

* A chapter has THREE numbers. The database row id is what filenames use,
  chapter_index is reading order, and the distribution track number is a third
  thing again. Only the row id survives an edit. Front and back matter are
  numbered with roman numerals in the MP3 filenames.
* The narrator is written three ways, and the most common way is nothing at all.
  1,803 utterances have a NULL speaker_name, meaning narrator; 614 say
  "narrator" and 15 say "Narrator". A join that skips nulls loses most of the
  narration silently. Utterances now carry speaker_name_raw, the resolved name,
  and speaker_resolution, and the two spellings are not merged - that is a
  decision for whoever owns speaker identity, not something an export should do
  quietly.
* A .wav file is not always a WAV. 30 of the 91 voice reference files are MPEG.
  Every render reports the container measured from the bytes, with
  extension_matches_container, so a reader cannot be misled by the name.

A validator proves the bundle is internally consistent; it cannot prove the
bundle is true. scripts/audit_controller_bundle.py re-measures every hash, byte
size, duration and count against the project on disk - 66,011 claims, all
holding. It caught two real defects in the first draft of this bundle, including
one of my own: produced_at was stamped with the build time, which is a claim
about the audio that nothing supported.

The validator was also run against seven deliberately broken bundles to confirm
it can fail. A validator nobody has seen fail is not evidence of anything; those
cases are now tests.

scripts/audio_controller_connect.py runs the T01-T10 suite: 4 pass, 1 fails,
5 are blocked. T04 fails honestly - there is no ingest endpoint, and nothing was
pushed. T05-T08 are blocked because the controller's own plan sequences ingest
after the metadata arrives; a proposed contract is included rather than an
invented endpoint. T02 is blocked: no node key or service token is configured
here, and an operator has to supply one.

Two corrections to the node profile, which the controller reads: it claimed the
web UI has no authentication (a session gate was added 2026-10-09) and that the
controller host is offline (it is reachable and serving umbratrans-web 0.1.0).

Full suite 751 passed, 8 failed - the same pre-existing pronunciation and
accent failures, unchanged. ruff and mypy clean on everything added.
2026-10-14 10:56:32 -07:00

185 lines
7.9 KiB
JSON

{
"_comment": [
"Umbra node profile - WHAT THIS MACHINE IS, declared by this machine.",
"Produced under spec 21 (specs/21-umbra-system-nodes-tasks-and-changes.md).",
"Read-only fact check: .venv/bin/python scripts/umbra_node_report.py --check",
"This file contains NO secrets. Only ids, names, labels and counts.",
"'status: draft' means the controller has not yet ratified this node."
],
"node_contract_version": 1,
"status": "draft",
"identity": {
"node_id": "umbraaudio-m4",
"display_name": "UmbraTools - Mac mini M4 (UmbraAudio)",
"organization": "umbra",
"role": "worker",
"products": ["umbraaudio"],
"primary_product": "umbraaudio",
"timezone": "America/Los_Angeles",
"operator": "single human operator (B.D. Murphy)"
},
"host": {
"hostname": "mini.bdmurphy.me",
"os": "macOS 26.6.2 (arm64)",
"lan_ipv4": ["192.168.123.12", "192.168.123.154"],
"storage_root": "/Volumes/1TB_APL_Storage",
"home_relocated": "/Volumes/Dock/worstauthor",
"python": "3.12.13"
},
"services": {
"web_ui": {
"bind": "0.0.0.0:8420",
"stack": "FastAPI + HTMX",
"public_name": "umbraaudio.bdmurphy.me",
"exposure": "Caddy reverse proxy on ANOTHER LAN machine -> this port",
"auth": "HTTP Basic session auth (umbraaudio/auth.py, PBKDF2-HMAC-SHA256) enforced by middleware in umbraaudio/web/__init__.py, fail-closed. added 2026-10-09.",
"autostart": "launchd LaunchAgent com.umbraaudio.web at /Library/LaunchAgents (system-wide, KeepAlive)"
},
"tts_local": {
"url": "http://localhost:8881",
"engine": "Qwen3-TTS (transformers, MPS)",
"autostart": "launchd LaunchAgent com.umbraaudio.qwentts (KeepAlive). LM Studio is MANUAL by design: an LLM model and the TTS model cannot be co-resident in 16 GB."
},
"tts_relay_listener": {
"status": "RETIRED 2026-10-08. The GPU box 192.168.123.14 was taken offline and the root relay removed; port 18881 is closed. Local :8881 is the only TTS backend.",
"note": "Do not re-add qwen_tts_helper_url or :18881 speculatively; re-add a TTS backend only when the controller exposes one."
},
"llm_local": {
"url": "http://localhost:1234/v1",
"engine": "LM Studio, google/gemma-4-e4b",
"note": "MUST NOT be co-resident with local Qwen3-TTS (16 GB unified memory)"
}
},
"capabilities": [
"audio.parse_manuscript",
"audio.segment_utterances",
"analysis.character_discovery",
"analysis.speaker_attribution",
"analysis.speaker_clarity",
"analysis.name_token_scan",
"character.personality_analysis",
"character.consistency_check",
"voice.capture_character",
"voice.reference_generation",
"voice.attribute_measurement",
"audio.render_utterances",
"audio.stitch_chapters",
"audio.normalize_loudness",
"export.m4b",
"export.mp3_package",
"review.speaker_QA"
],
"capability_status": {
"_note": "Declared per controller §2.3 as a vocabulary even with no scheduler behind it. Status is honest about what is real today.",
"character.personality_analysis": "declared_not_implemented - what exists is unversioned LLM prose in speakers.json, not a versioned artifact (alignment phase 6b finding B-03)",
"character.consistency_check": "declared_not_implemented",
"voice.capture_character": "partial - vmatrix capture exists but is name-keyed, locale-blind, and absent for the active project (manifest.md §5.4)"
},
"resource_pools": {
"cpu": {"capacity": "bounded; shares the same 16 GB as TTS", "concurrency": 1},
"llm": {"capacity": "1 x gemma-4-e4b (MPS, ~21 tok/s)", "concurrency": 1},
"tts_mps": {"capacity": "1 x Qwen3-TTS-12Hz-1.7B", "concurrency": 1, "note": "one orphan generation can stall the model lock for ~20 min"},
"gpu_cuda": {"capacity": 0, "note": "no local NVIDIA GPU; GPU work is delegated to 192.168.123.14"},
"ffmpeg": {"capacity": 1, "note": "launchd PATH must be extended or export fails"}
},
"constraints": [
"macOS Local Network TCC blocks launchd-spawned processes from all LAN IPs (EHOSTUNREACH); loopback is exempt",
"external-volume TCC: only .venv/bin/python may read/write /Volumes/1TB_APL_Storage under launchd; a shell cannot even open a script there",
"Qwen3-TTS and LM Studio must never be co-resident",
"PROJECT_BASE is hard-coded in umbraaudio/config.py and is not configurable",
"four divergent DDL authorities in this repo (see manifest.md 1.3)"
],
"projects": [
"DealHunterShip_4.0e_whole-to-audio",
"Last Dwarf",
"Treasure Island",
"Vocals"
],
"project_lifecycle": {
"DealHunterShip_4.0e_whole-to-audio": "released",
"Last Dwarf": "active",
"Treasure Island": "experimental",
"Vocals": "showcase"
},
"schema_fingerprints": ["399285355e2d456e", "b7c9ae76bc6615bd"],
"_schema_fingerprints_note": [
"Two distinct fingerprints = real per-project schema drift, deliberately NOT normalised.",
"399285355e2d456e: current shape (has name_tokens, the 5 clarity columns, and source_unit_key) - Last Dwarf, DealHunter.",
"b7c9ae76bc6615bd: older shape (no name_tokens, missing 9 utterance columns) - Treasure Island, Vocals.",
"History: 0078569a0fa6bc70 / 367d5df9e546dbba before 2026-10-03. The change is the",
"additive source_unit_key column; the drift detector reported it rather than hiding it.",
"Produced by scripts/umbra_node_report.py; re-run --check after any migration."
],
"authoritative_data_summary": {
"in_sql": ["project", "chapters", "utterances", "name_tokens"],
"in_json": [
"speakers.json",
"voice_profiles.json",
"voice_status.json",
"voice_engines.json",
"phonetic_names.json",
"m4b_meta.json"
],
"on_filesystem_only": ["voices/*_ref.wav", "voice_prompts/*.pt (vmatrix)", "chapter_audio/**/*.wav", "*.wav.txt markers"],
"never_import_as_authoritative": ["pipeline_status.json", "*_build_status.json", "manifests/*.json", "discovery_pass*.json"]
},
"facts_for_the_controller": {
"empty_sql_tables": [
"characters",
"generated_audio",
"pronunciation_guide",
"quality_reports",
"review_decisions",
"exception_reviews",
"voice_review_history"
],
"has_stable_utterance_id": true,
"source_unit_key": {
"status": "implemented and backfilled 2026-10-03",
"derivation": "sha256('uk1' | chapter_source_text_sha256 | occurrence | text_sha256)",
"project_agnostic_by_content": true,
"coverage": "10015/10015 Last Dwarf, 4319/4319 DealHunter, 3811/3811 Treasure Island, 36/36 Vocals",
"collisions": 0,
"renumbering_proven_safe": "chapter 1 renumbered +100 -> 0 keys changed",
"resolvable_without_index": "alignment phase 3: 200/200 units resolved, 0 used utterance_index"
},
"has_stable_speaker_fk": false,
"has_user_accounts": false,
"has_shared_postgres_client": false,
"has_heartbeat_or_lease": false,
"controller_stage_reached": "0 verified 2026-10-03 (health 200, heartbeat 401, no-Host 400 - all as documented)",
"test_suite": "pytest, 751 passing at 2026-10-14 (8 pre-existing failures: 2 test_pronunciation helper_url mock, 6 test_voice_profile_manager accent)",
"open_defects_blocking_exposure": [
"no authentication on any endpoint (spec 20 J.6) -- RESOLVED 2026-10-09, auth gate added in umbraaudio/web/__init__.py",
"download path traversal via str.startswith (routes.py ~5049)",
"upload filename not sanitised with Path(...).name",
"no CSRF, no security headers, no TrustedHost"
]
},
"contracts": {
"informational_only": {
"min_node_contract_commit": "4775e3b",
"note": "git HEAD moves constantly; this field is documentation, never drift-checked"
},
"specs": [
"specs/21-umbra-system-nodes-tasks-and-changes.md",
"specs/20-enterprise-database-integration.md",
"specs/19-enterprise-control-plane.md",
"specs/18-UmbraTrans.md",
"manifest.md"
]
}
}