Two groups of changes, committed together as the follow-up to the initial push: 1. .gitmodules — submodule Qwen3-TTS now points at the internal Gitea fork (ssh://git@mini-eq:2222/umbragit/Qwen3-TTS.git) instead of upstream GitHub, so a clone actually gets the UmbraAudio branding/integration commit 012e452 rather than vanilla upstream. 2. New infra/ tooling for the cross-machine work in spec 21: agent_bridge, remote_credential_broker (Keychain-backed, secrets read at runtime and generated via read_secret/random_secret — no hardcoded credentials), the enterprise bootstrap scripts, and the oldbox SSH/auth prompts. Plus backup/ restore scripts, two specs (18 UmbraTrans, 19 enterprise control plane), umbraaudio/voices/lmstudio_gate.py, and in-flight edits to the discovery/attribution/profiling modules, config, chapter_builder, emotion, pronunciation, qwen_tts and routes. Also adds *.db to .gitignore: SQLite project databases are runtime state and no .db file was tracked before. Verified before staging: .env's only change is UMBRA_TTS__QWEN_TTS_HELPER_URL and the committed copy has zero secret-looking values; the broker ships only config.example.json.
5.5 KiB
Build a secure oldbox ↔ Mac agent communication MCP server
You are the OpenCode agent running on oldbox. The Mac-side agent needs a
reliable way to exchange sanitized instructions and reports with you without
requiring repeated clipboard copy/paste.
Objective
Create a standalone, local stdio MCP server named agent-comm under the
UmbraTrans workspace on oldbox. It must use a shared, append-only inbox/outbox
on the NAS2 netbackup/UmbraTrans share. Do not create an unauthenticated
network listener.
The first purpose is to send the pending SSH-authentication diagnostic report back to the Mac agent without exposing passwords or other secrets.
Security requirements
- Never accept, return, log, or transmit passwords, sudo passwords, private
keys, API tokens, Keychain data, or
/etc/shadowcontents. - No secret values may appear in MCP arguments, environment variables, command lines, filenames, reports, or logs.
- The server must be stdio-only. It must not listen on a TCP or UDP port.
- Use the NAS share only for sanitized text/JSON messages.
- Message files must be written atomically (
temporary file+rename). - Use restrictive directory/file permissions and refuse symlink paths.
- Validate and bound all message sizes.
- Do not change SSH, sudoers, firewall, Docker, Qwen, or existing services.
- Do not install a system-wide daemon yet.
- Do not grant
NOPASSWD: ALLor any broad sudo rule.
Discovery phase
Before creating the server, perform read-only discovery:
- Locate the NAS2 mount for the
netbackupshare. - Locate or propose the
UmbraTranssubdirectory. - Confirm the path is writable by
umbra. - Report the exact path and permissions.
- Do not write any message until the path is confirmed in the report.
Use a conservative search such as find, mount, and df; do not modify
mounts or credentials. If the share is not visible, report that and stop rather
than guessing a path.
Shared mailbox layout
Once the path is confirmed, use this layout below the confirmed UmbraTrans directory:
agent-mailbox/
inbox/ # sanitized instructions from Mac to oldbox
outbox/ # sanitized reports from oldbox to Mac
processed/ # messages already handled by oldbox
archive/ # optional processed message archive
Each message is one UTF-8 JSON file with this shape:
{
"schema_version": 1,
"id": "UUID",
"created_at": "ISO-8601 UTC",
"sender": "oldbox",
"kind": "diagnostic|report|instruction|ack",
"correlation_id": "optional UUID",
"title": "short non-secret title",
"body": "sanitized text, maximum 64 KiB",
"attachments": []
}
Do not allow arbitrary attachment paths in version 1.
MCP tools
Implement these tools:
agent_status
Return only:
- server version
- configured mailbox path
- whether inbox/outbox are readable/writable
- number of pending messages
- last health-check time
- no secret values
send_report
Arguments:
title: non-secret stringbody: sanitized report textcorrelation_id: optional non-secret stringkind: defaultreport
Write a validated JSON message to outbox/ using an atomic rename. Return the
message ID and path, never the secret contents.
read_instructions
Read up to a bounded number of JSON messages from inbox/. Validate each
message, return only sanitized metadata/body, and move successfully handled
messages to processed/ using an atomic rename. Never execute commands from a
message automatically.
send_ack
Accept only a correlation ID and short non-secret text. Write an ack message
to outbox/.
Current diagnostic report
After the server is implemented and tested, use it to send one report covering:
- the successful manual SSH path
- the broker's
Permission denied (publickey,password)result - the fact that no authorized key or certificate is configured
- the account/password state already observed
- the exact unresolved question: whether the broker supplied a wrong password or supplied no usable password to OpenSSH
Do not include any password or hash. Do not ask the Mac agent to guess.
Implementation requirements
- Python 3.11+ standard library is sufficient; do not add dependencies unless necessary.
- Use type hints,
from __future__ import annotations, logging to stderr, and noprint()for debug output except the CLI's intentional status output. - Include a README with the exact OpenCode local-server configuration.
- Include unit tests for JSON validation, atomic writes, path traversal, symlink refusal, size limits, inbox/outbox behavior, and secret-pattern redaction.
- Add a
--checkmode that performs only read-only checks. - Run the tests and
--checkbefore sending the first report.
OpenCode configuration to document
The configuration should be equivalent to:
{
"mcp": {
"servers": {
"agent-comm": {
"type": "local",
"command": [
"/absolute/path/to/python",
"-m",
"agent_comm"
],
"environment": {
"AGENT_COMM_ROOT": "/confirmed/nas2/path/agent-mailbox"
}
}
}
}
}
Do not add secrets to the configuration.
Completion response
Return a concise report containing:
- Confirmed mailbox path and permissions.
- Files created.
- Test results.
- OpenCode configuration path/snippet.
- The first outbox report message ID.
- Any unresolved issue.
Do not claim the Mac agent has received the message until the Mac-side reader has been configured and verified.