# Trust boundaries and wire protocol ## Direct-companion reviewed feedback The authenticated single-owner companion stores exact-result feedback and reviewed preference rules privately in a 0600 SQLite file. Its text-only learning provider has no tools or Vault access. Only explicit acceptance adds scope/expiry-bound preferences to private task/chat execution; discovery is a separate opt-in. The scheduled-job plugin keeps the existing assembled-prompt scanner and skips shared/origin deliveries. Rules cannot grant tools, recipients, schedules or external actions. Owner binding, revision checks, receipts and live revocation checks are direct-profile mechanisms, not independent encrypted custody. See [storage, disclosure and separate activation](engineering/job-feedback-learning.md). Brokered Operanda routes, coordinator/model separation, release gates and CON-02/CON-13–16/BLK-02 remain unchanged. ## Direct-companion address suggestions The separate Expo/Hermes address form can disclose actively edited street text, the chosen country and a selected suggestion to Google through its authenticated configured connector. The phone shows that disclosure and an off switch before typing. Opening a stored address sends no address; the service never reads other Vault records. The Google key remains server-only. Requests are bounded, rate limited, non-caching and directed to fixed HTTPS endpoints; responses and errors are not logged. The feature remains server opt-in, with manual fallback. See [setup and limitations](address-typeahead.md). This does not change the coordinator, Operanda release gates, encrypted Vault protocol, private-record authorization or CON-13–15/BLK-02 findings. Brokered mode does not use these routes. **Target boundary update, 23 September 2026:** [Operanda's cross-app Vault decision](engineering/cross-app-vault.md) requires an isolated Operanda display surface, encrypted fields for OWP/task agents, and direct authorized plaintext delivery to end services. The alpha described below still exposes selected plaintext to its client and harness; it does not implement that target. The coordinator remains unable to decrypt or invoke models. No gate or wire profile changes with this documentation. This document describes the implemented alpha, not the future security promise. All fixture accounts delegate their content to **one trusted Mac node**. Application authorization prevents one fixture account reading the other's private space. The Mac administrator can read both in-process; this is not independent household key custody. ## Data flow ```mermaid flowchart LR I[iOS / dev browser] -->|signed HPKE command| C[Coordinator relay] C -->|opaque command| V[Vault Agent] V -->|ciphertext + metadata| C C --> D[(PGlite PostgreSQL)] V --> K[(Encrypted node journal)] V -->|explicit context| H[Fixture or loopback Ollama] H -->|draft content| V V -->|signed HPKE response via relay| I ``` The node holds its signing key, a P-256 transport key and X25519 record-wrapping key. Each record revision has a random AES-256-GCM DEK and 96-bit nonce. The JSON header is JCS-canonicalized as AAD. HPKE X25519/HKDF-SHA256/AES-256-GCM wraps the DEK to the node. An Ed25519 signature covers header, ciphertext, nonce and key wrap. Signature, identity and exact header binding are checked before plaintext is used. Library: `@hpke/core`; no hand-written KEM/KDF implementation. The node keybag and command/grant journal are encrypted with AES-256-GCM. Scrypt (N=32768, r=8, p=1) derives its key from a Keychain-held random password or an operator-supplied password. Writes use a 0600 temporary file, fsync, atomic rename and directory fsync. One process owns one journal. This software keybag is not a Secure Enclave or owner-recovery scheme. Transport uses Apple's named P-256/HKDF-SHA256/AES-256-GCM HPKE suite for iOS interoperability. The bytes used as AAD are five UTF-8 fields joined by LF, with no terminal LF: profile, direction, device_id, request_id, sent_at. Signature bytes append LF + Base64(enc) + LF + Base64(ciphertext). UUIDs and direction are schema validated; timestamps use UTC ISO8601. A new HPKE context encrypts each message exactly once. Signature and encryption keys are distinct. Responses bind request ID, device ID and direction. Requests expire after a five-minute clock-skew window. Enrollment certificates expire after seven days; the node also checks its current enrolled-device set on every command. Mutation idempotency is scoped by principal + request UUID and the canonical decrypted command digest. A reused ID with different content is a conflict. Snapshot reads do not enter the durable mutation journal. Pairing manifests contain a 192-bit single-use code, principal scope and node public keys. They must be obtained through a trusted out-of-band path; do not trust a manifest supplied by an untrusted relay. Enrollment is operator provisioning of fixture identities, not OIDC authentication or proof of ownership. HTTP is allowed only on loopback. All non-loopback deployments remain unsupported; native clients require HTTPS there. ## Coordinator-visible fields | Object | Visible fields | Encrypted content | |---|---|---| | Record header | Profile, record/space IDs, revision, author fixture name, timestamp, node signer ID | Record kind, title, text, tags, policy, source links, runtime binding, deletion flag | | Work | Work/space/spec/result IDs, principal, state/revision, fence, lease, attempt count, error code, timestamps | Objective, title, selected context revisions and result | | Events/outbox | IDs, space, work ID, fixed event kind, timestamps | No free text is accepted | | Device certificate | Device ID, fixture principal, public keys, expiry, node ID, signature | No private key is transmitted | | Command envelope | Device/request IDs, direction, profile, timestamp, signature, HPKE enc/ciphertext | Entire command and response body | | Grant | None persisted in coordinator tables beyond encrypted receipt records | Record selection, recipient, expiry, revocation, evidence | Sizes, counts, timing, relationships and access patterns remain visible. Payload digests live inside the node journal, not as public low-entropy commitments. The coordinator sees no raw payment data because payment types and execution do not exist in the alpha. `packages/contracts/visibility.ts` is compile-time exhaustive for payload, certificate and record/transport headers; schemas include these annotations. It is not coverage of the unavailable merged 0.1+0.2 bundle. ## Store ownership and crash behavior The coordinator is authoritative for work state and lease/attempt metadata. Work transition, event and outbox creation share one SQL transaction. A lease has database-derived expiry and an increasing fence; a stale or cancelled attempt cannot finish work. The node polls for work, checks signed content and scope, invokes a harness, stores encrypted output and requests transition to `waiting_dependency`. Only the owning principal can accept the result. In this alpha the terminal name is `completed`; the original spec's `succeeded` mapping is deliberately isolated in the alpha profile. The node is authoritative for enrollment, grant activation/revocation and its mutation journal. A prepared encrypted record is journaled before the coordinator write, so a retry can reuse exact ciphertext after a crash. A grant covers one record revision and the fixture service, with a maximum one-hour lifetime. It has no attenuation, dynamic tag scope, delegated signer, external payment or provider side effect. Revocation stops subsequent delivery but cannot recall copies already delivered. There is **no money ledger or external-effect journal**. BLK-01's proposed two-journal protocol is not implemented by a misleading local transaction. No model can directly call a connector. GRANT delivery only produces encrypted evidence for a recipient on this node. ## Threat model for this configuration | Adversary | Control | Residual exposure / limit | |---|---|---| | Passive coordinator DB/log/backup reader | AEAD ciphertext, no keys; no request-body logging | Metadata above; availability and rollback remain possible | | Malicious active coordinator | Signed record content; native pinned encrypted command channel | Work metadata and assignment sequencing are not a production authenticated consensus protocol; malicious relay can deny/replay metadata. Browser frontend is served by the coordinator and therefore trusts its code | | Other fixture account through API | Node principal/space checks, explicit shared audience, owner-only mutations | Both accounts still trust the same node administrator | | Mac administrator or compromised Vault Agent | No claim of protection | Both fixture accounts' plaintext/keys are accessible while the node is unlocked | | Browser script injection | Escaped user text, restrictive CSP, no third-party assets | The development browser remains a same-origin code trust boundary; use no real data | | Lost locked iPhone | WhenUnlockedThisDeviceOnly Keychain; no persisted plaintext cache | Secure Enclave custody and hardware/lock-state tests are pending; an unlocked compromised device can access content | | Network attacker | Signed HPKE content; loopback-only alpha; non-loopback client requires HTTPS | Loopback metadata and pairing bootstrap rely on trusted local provisioning | | Malicious model output / reference content | Model has no tool authority; result awaits owner acceptance | Text can be misleading; no claim that a prompt neutralizes all injection | | Hostile marketplace package | Execution disabled | Whole-process containment has not been selected or tested | | Backup thief | Encrypted node journal and ciphertext DB | Theft of unlock password plus journal reveals keys; no owner-recovery guarantee | The JS runtime cannot guarantee complete plaintext erasure because strings may be copied by the GC. Temporary byte buffers are cleared where practical. Compromise of a long-lived recipient key can decrypt stored wraps; no forward-secrecy claim is made. ## Known implementation boundaries - Node software keybag only; no per-user roots, membership epochs, recovery envelopes, key rotation or independent device custody. - No production distributed assignment relay, node credentials or signed coordinator fencing protocol. The internal HTTP storage port uses a random process token and loopback binding. - No offline mutation outbox, pagination, SSE/commit-ordered feed, tombstone propagation or scalable local index. Snapshots are small-fixture only. - An unexpected transport failure can leave a mutation outcome uncertain. The protocol supports retrying the same request ID; persistent client retry UX is a remaining package. Do not blindly create a new request after uncertainty. - Node-offline work stays queued; interactive unlock/disclosure lifecycle states are not implemented. No fallback is allowed. - PGlite's queue is local PostgreSQL SQL, not a claim of pg-boss, multi-process Postgres or fleet equivalence. - A crash between writing an encrypted result and finalizing work may leave an unreferenced ciphertext record; garbage collection is pending. No external effect is replayed. Production gating is explicit in `release-gates.ts`. Do not enroll real personal information or invite Madeline to depend on this instance yet. ## Matching previews `work.match` is an owner-only encrypted command for drafts. The trusted node validates work/space bindings and every selected context revision before compiling the objective and selected preferences. Matching reports and their input/roster digests live only in the encrypted node journal and encrypted responses. They are not disclosure grants or dispatch instructions. Stale inputs or roster/policy changes invalidate the displayed recommendation. The Vault composition accepts local scorers only. The separate synthetic evaluation CLI can call Jev with a request-bound, one-use trial permit; it has no Vault access, work engine or dispatch interface. It is not a production remote-processing path. No private-data processor gate was relaxed. Details and remaining admission boundaries are in [matching.md](matching.md). ## Selected job agents Catalog lookup and exact job-kind matching run inside the authenticated Vault endpoint and return encrypted replies. Work specifications can pin a built-in agent ID, version, definition digest, job kind and synthetic-preview mode. Those fields remain encrypted with the specification and are revalidated at execution and matching time. Shadow matching includes the selection in its brief digest and cannot change it. The manifest is a reviewed instruction profile, not an installed executable or a capability grant. Live execution remains blocked by the existing data, connector, effect and money boundaries. ## Authorized synthetic task matching on the Mac The selected development topology is Mac-hosted Operanda with hosted Jev. The ordinary Vault matching port still accepts local scorers only. A separate task adapter is enabled explicitly with `COZ_TASK_MATCHING=jev_synthetic`, a server-only TypeSafe key, and an operator-set daily request limit. `tasks.submit` and `tasks.rematch` can carry a signed/encrypted authorization bound to the immutable spec digest, pinned model, expiry, and at most two attempts (task and builder). Only the Christian fixture operator can use this trial; Vault context is rejected. The adapter sends the synthetic objective, trigger, acceptance criteria, required capabilities/tools/output schema and eligible candidate descriptions. It omits principal, space, task identifiers and Vault record contents. Attempt reservation, request digest and successful scoring evidence are encrypted in the node journal. Reservations survive failure/restart; an ambiguous attempt is never automatically sent again. A new explicit rematch consumes the same durable daily allowance. The limit counts requests, not currency; it is not a production budget ledger. Synthetic declarations are operator assertions, not a personal-data classifier. This trial does not enable personal-data processing, device enrollment outside loopback, execution, schedules, watchers, package installation or delegation. ## Account review profile The development launcher's coordinator child now receives an explicit environment allowlist: OS runtime paths and relay transport/storage settings, including its internal process token. It does not inherit Twilio or model credentials, Vault unlock material, unrelated provider secrets or runtime injection variables such as `NODE_OPTIONS`. The trusted node retains its own configuration. A real child-process regression verifies this separation; it is not whole-host filesystem isolation. The optional account route now relays device-signed HPKE envelopes before enrollment. Outer device public keys must match the encrypted payload. Phone/OTP and keybag data are endpoint-only. Approved SMS verification releases a device-bound challenge; account-key proof is independently required for enrollment. UUID account principals have separate private spaces and cannot access the fixture household. See [account protocol, recovery and remaining gates](engineering/operanda-accounts.md). The trusted node still holds readable account data when unlocked; production and shared custody claims remain blocked. No real-data/recovery gate is lifted.