The audio node is the master for the audiobooks. UmbraTrans holds the metadata needed to reason about a file and a reference to where it is; it does not hold the bytes. DECISION-6 (object storage) stays unresolved, and this is what lets the system be useful before it is resolved rather than after. POST /api/compute/artifacts takes a manifest against a project and declares each entry: content hash, size, media type, kind, and a storage_uri naming where the file actually is. GET /api/compute/artifacts lists the catalogue. There were no artifact routes at all before this - the tables existed since migration 0003 and nothing had ever written to them, so "the controller has the artifacts to show a job is complete" had nothing behind it. Every declared row lands integrity='unverified', and that is the point rather than a caveat. Ingesting is a claim: a node reporting the hash of a file is a statement about bytes nothing here has read, because there is no object storage to read them from. A design where submitting a manifest marks the catalogue verified would make the first ingest the most reassuring and least true event in the system's life. verified_at is set by reading the bytes back and hashing them, and nothing in this path does that. storage_node_id is part of the reference rather than an afterthought, because a bare path means nothing six months later: /audio/book.mp3 is on whichever machine happened to serve it that day. Idempotent on (project_id, content_hash), so the same manifest can be submitted twice, or by two operators who did not talk to each other, without the catalogue doubling - and a partly-present manifest reports what was new rather than just "done". An operator who submitted 300 entries and was told "done" would not know two were malformed and dropped, so malformed entries are named in the response instead. The listing is scoped by organization on every call. artifacts is platform-level, as every table a node serves from is, so a missing predicate there is a cross-tenant read rather than a style issue. A project in another tenant's schema is refused as *not found*, not as *not permitted*, because a refusal that says "this exists but is not yours" answers the question the caller was actually asking. Migration 0007 applied to the deployment. Mutation-verified: marking an ingest verified fails 1, dropping the idempotency check fails 2, and dropping the organization predicate from the listing fails 7. 704 passed, 4 skipped.
UmbraTrans local bootstrap workspace
This workspace contains the unprivileged agent-comm server requested by the Oldbox handoff. It does not contain the UmbraTrans application source: no application bundle was present locally. A staged archive, if present, is left untouched.
Layout
agent_comm/— Python 3.11+ standard-library, stdio-only MCP server.agent_comm/tests/— dependency-free unit tests.umbratrans_web/— phase-1 account/user-management backend slice (FastAPI, Argon2id, sessions, admin routes, in-memory and local JSON development repositories). It does not connect to PostgreSQL; seeumbratrans_web/README.md.umbratrans_web/migrations/— proposed, unapplied phase-1 identity SQL and its rollback.opencode.json— non-secret OpenCode registration for the confirmed NAS2 mailbox.agent-comm.local.example.json— non-secret local fallback registration..env.example— non-secret environment template.DATA-SEPARATION.md— development/production boundary.
Confirmed NAS2 mailbox
The oldbox desktop account mounted NAS2 netbackup through its user GVfs session and confirmed the UmbraTrans directory readable, searchable, and writable. The server requires the explicit NAS deployment mode, the enclosing GVfs mount, and an affirmative mount confirmation:
/run/user/1002/gvfs/smb-share:server=nas2.local,share=netbackup/UmbraTrans/agent-mailbox
Run read-only health checks:
cd /home/umbra/src/UmbraTrans
AGENT_COMM_MODE=nas \
AGENT_COMM_NAS_MOUNT=/run/user/1002/gvfs \
AGENT_COMM_NAS_CONFIRMED=1 \
AGENT_COMM_ROOT='/run/user/1002/gvfs/smb-share:server=nas2.local,share=netbackup/UmbraTrans/agent-mailbox' \
/home/umbra/.local/bin/uv run python -m agent_comm --check
If the user GVfs session is absent, do not guess another path or broaden share permissions. Use the private local fallback documented in agent-comm.local.example.json, or have a privileged operator restore the confirmed user mount.
The server speaks newline-delimited MCP JSON-RPC over stdin/stdout. It opens no network socket and never executes queued instructions.
OpenCode registration
opencode.json contains the exact NAS configuration and paths only. It must never contain passwords, tokens, private keys, or other secrets. A Mac-side reader still needs separate configuration and verification before an outbox message can be considered received.
Development workflow
The project uses uv and a workspace-local virtual environment:
cd /home/umbra/src/UmbraTrans
/home/umbra/.local/bin/uv sync --python /usr/bin/python3
/home/umbra/.local/bin/uv run python -m unittest discover -s agent_comm/tests -v
/home/umbra/.local/bin/uv run ruff check .
/home/umbra/.local/bin/uv run ruff format --check .
/home/umbra/.local/bin/uv run python -m compileall -q agent_comm
make check
The tests cover schema and size validation, atomic no-overwrite writes, restrictive modes, path traversal, symlink refusal, secret-pattern redaction, inbox processing, and read-only initialization.
The account-management slice has its own targets:
cd /home/umbra/src/UmbraTrans
/home/umbra/.local/bin/uv run pytest umbratrans_web/tests
make web-check
Its tests use the in-memory repository and generated per-run passwords; they never read a production configuration, never connect to a database, and contain no credential material.
Backups
scripts/umbratrans_backup.sh is installed root-owned as /usr/local/sbin/umbratrans-backup and scheduled by the enabled umbratrans-backup.timer. It writes only to the confirmed NAS2 netbackup/UmbraTrans/backups tree:
- PostgreSQL:
pg_dumpcustom-format logical dump, never a live data-directory copy. - Configuration: PostgreSQL configuration files only.
- Workspace: source/configuration archive excluding
.venv, Git internals, runtime data, local mailboxes, and secret-bearing files.
Each file is staged outside the share, published with an atomic rename as umbra, checksummed, and verified after publication. Retention is 30 days for PostgreSQL/configuration/manifests and 14 days for workspace archives; stale temporary files older than one hour are removed. The timer fails closed if the user GVfs mount is absent and does not mount or broaden the NAS share.
The current first-run backup set is present under:
/run/user/1002/gvfs/smb-share:server=nas2.local,share=netbackup/UmbraTrans/backups
Check timer status with systemctl status umbratrans-backup.timer and the last run with journalctl -u umbratrans-backup.service.