MCP setup is owner-mediated. Miller shows the server identity, endpoint class, and declared tool metadata before enabling a server. The broker admits read-only calls automatically only under the read-only policy. Changing, unknown, or insufficiently declared calls prompt under ask-before-changes and execute only after an owner approval. Fully trusted mode is a separate explicit policy choice. Each classification, approval, denial, and bounded result is recorded in the broker's audit stream; provider payloads and secrets are not copied into that audit stream.
Codex typed reasoning, Codex Live sideband delegation, and Pi use the same broker and policy boundary. Account-backed apps are Codex-only. Provider portability covers the typed reasoning adapters and Pi route, not the installation or management of a Codex account app.
Task 18 tested official Codex CLI/App Server 0.146.0 on Apple Silicon; this is
the v0.1.2 minimum tested/support boundary. The 0.145.0 material is protocol
reference/evidence only, not a runtime support claim. Packaging requires the
explicit lockfile-integrity dependency bootstrap and never calls it implicitly.
Protocol reference: 0.145.0; tested runtime: 0.146.0.
The release verifier fails closed on wake archives, headers, compiler inputs,
private keyword files, Codex or Rust build artifacts, fake helpers, fixtures,
transcripts, runtime logs, sockets, and credential material. It allows only
the five exact verified model/token files under WakeWord/model and the
linked native wake code in Miller. The application SBOM and runtime inventory
name Sherpa-ONNX, ONNX Runtime, and those exact assets.
Wake uses one AVAudioEngine capture owner and a process-local microphone lease. The lease is released before Live starts. Permission failure, input-device loss, sleep, disable, or shutdown stops the engine and clears callbacks; ordinary application-focus changes do not. Generation checks reject stale samples and late callbacks. Prepared command PCM is bounded and can be injected into the existing WebKit outbound track once; no second process or speech backend is created.
The default wake phrase is Hey Miller; one bounded custom English phrase is compiled locally. The ordinary owner-visible wake flow passed, including custom-phrase activation and post-Live rearm.
Live Voice depends on an owner-installed official Codex App Server. Missing or incompatible Codex, denied permission, unsupported model, provider refusal, timeout, malformed protocol data, or helper termination fails closed for the live session while leaving typed operation and local history available.
Miller owns persistence. It stores versioned, bounded credential payloads as
generic-password items under Keychain service
ai.millrace.miller.credentials. Items use credential-reference UUID accounts,
AfterFirstUnlockThisDeviceOnly accessibility, no synchronization, and the
application's default access group. SQLite stores provider labels, endpoints,
models, and credential references. SQLite does not store secrets.
Ad-hoc development bundles are qualified only with synthetic Keychain items. Stable signed-identity access across upgrades remains a Gate 4B requirement. Local logout removes local helper and Keychain state. It does not claim remote provider revocation.
The Node helper is part of the trusted computing base, not a sandbox. Miller
starts it without a shell, passes only LANG, LC_ALL, TMPDIR, and TZ,
and communicates through a strict, size-bounded JSONL protocol. Standard error
admits only bounded, non-content diagnostics. Raw exceptions, provider bodies,
URLs, headers, prompts, responses, tokens, and stack traces are not retained.
Reasoning requests require an empty tools array. The helper contains no
model-callable shell, filesystem, coding-agent, or arbitrary tool surface.
Remote OpenAI-compatible endpoints must be HTTPS and may not contain user
information, queries, or fragments. Authenticated redirects are refused.
Loopback HTTP is admitted only for controlled local fixtures. OAuth binds to
127.0.0.1, requires PKCE and matching state, admits one callback, and uses a
bounded lifetime.
Signing, notarization, clean-machine distribution, upgrade identity, live microphone/audio observation, and public-release security qualification remain open release gates.
Miller links the reviewed MillerAvatarCore and MillerAvatarHost package
products in process. It does not start an Avatar daemon, sidecar, XPC service,
or second audio renderer. Avatar is off by default, and a package link alone
does not construct WebKit, read an asset, or request a permission.
Model and motion selection is owner-mediated. Native admission captures a regular user-selected file into bounded immutable memory. It rejects malformed, external-resource, symlink, non-VRM 1.0, non-VRMA 1.0, and over-budget content. WebKit receives only a session-bound local URL. Three consecutive failures quarantine only the affected model or motion. A motion failure cannot quarantine a valid model or disable Miller.
The renderer uses an ephemeral WebKit data store, a fixed five-file local resource bundle, a network-closed content-security policy, fail-closed navigation, a typed command receiver, and generation-fenced callbacks. Miller's current source-first package adds no App Sandbox network-client entitlement. VRMA controls skeletal tracks only. Miller retains semantic phase, expression, gaze, playback, and mouth-cue authority.
Renderer, bridge, asset, and animation failures are Avatar-only failures. Typed reasoning, Live admission, history, settings, capability approvals, and tool execution remain independently available.
The direct comparator admits only the selected codex_oauth profile. The bounded
access token and account ID loaded by GPTLiveCredentialLoader remain in
memory, are sent only in the direct /v1/live HTTP and sideband WebSocket
headers, and are never logged, persisted, stringified, or included in fixed
error codes. API-key authentication is refused by the direct route. The
multipart SDP/session body, SDP answer, call ID, and sideband frames have
explicit size and content bounds; provider response bodies and event error
messages are not retained.
The direct route uses Foundation URLSession and
URLSessionWebSocketTask. It validates the successful 2xx answer and supported call-ID
headers, maps HTTP and protocol failures to fixed Miller codes, retries only
sideband startup within a small bound, buffers only a fixed number of early
frames, and closes on cancellation, expiry, unexpected closure, malformed or
binary frames, and teardown. Unknown valid event types are bounded and
ignored. Transcript state is bounded presentation state and never becomes a
second persistence model. Client delegation is injectable, superseded by a
newer delegation, and returns a fixed unsupported spoken outcome when no safe
Miller reasoning consultation is supplied.
The MillerLive process supervisor accepts only an absolute executable path,
uses no shell, creates isolated task-private process roots, and signals the
helper process group on timeout, cancellation, failure, and parent shutdown.
The only persistent-looking helper input is a task-private mode-0600
CODEX_HOME/config.toml containing the non-secret realtime_conversation
feature enablement and exact V1 realtime selection; it is removed before
termination is published. The helper receives the exact reviewed PATH and
locale values.
Its strict decoder bounds frames, transcripts, audio chunks, and event counts;
rejects unknown fields and methods; and fences request, thread, generation, and
terminal state.
Output buffering is fixed at eight frames and fail-closes on overflow. All
retained event strings, including roles, thread identifiers, terminal reasons,
and transcript text, share a cumulative bound. Account lifecycle notifications
are validated against their exact enums and discarded without logging their
contents; raw realtime items are likewise shape-checked and discarded.
Input writes, close, and reaping share one transport lock, and the input pipe
uses descriptor-local F_SETNOSIGPIPE instead of modifying global signal
disposition.
Exact 0.145.0 startup responses and notifications are independently correlated,
so either legal delivery order is bounded without accepting duplicates. A stop
during any starting phase cancels supervision locally; a protocol stop is sent
only for a matching session that has reached active.
The WebRTC v3 route uses an owner-installed official Codex CLI as an external
runtime. Miller does not bundle, build, download, update, or remove it. Before
readiness, credential access, WebKit peer creation, or launch, Miller resolves
the selected launcher to a native executable and requires an arm64-only Mach-O
with identifier codex, OpenAI team identifier 2DC432GLL2, and a valid
Developer ID chain. External updates may change the version and CodeDirectory
hash, so neither is pinned. Compatibility remains fail-closed at the App Server
protocol and WebRTC capability handshake.
Static preflight is not process identity. Immediately after posix_spawn, the
supervisor closes its copies of child-side pipe ends and asks the kernel for the
actual guest by PID. A short retry is permitted only while that guest is not
yet observable. The kernel-identified guest then undergoes strict
execution-requirement validation and is bound to the exact selected canonical
executable; its signing identity and architecture are compared again. This
occurs before
Miller publishes process state, starts pumps, or sends a protocol or credential
byte. Any failure kills and reaps the process group, closes parent descriptors,
and removes the private root. The shell qualification script is therefore
defense in depth, not the authority that permits OAuth credential delivery.
The packaged development app links MillerLive and MillerLiveAudio, supports
saved or automatic runtime selection plus an explicit development override,
and advertises the capability marker. It includes
the reviewed, hash-verified Node runtime but packages no Codex, Cortana, or
third-party WebRTC executable. The source plist does not contain the marker.
Ordinary launches configure no child process and construct no peer or WebKit
media session. They do not contact the provider, request microphone access, or
load the OAuth credential until Live Voice starts. Missing or incompatible
Codex makes Live Voice unavailable without disabling typed operation.
The live credential loader reads only the selected codex_oauth profile's
generated Keychain reference when Live Voice starts. It requires the exact
version-1 envelope and closed OAuth field set. The direct route uses the
admitted access token and account ID without a refresh or second auth store;
the retained App Server client uses the existing Miller/Pi refresh path and
rejects account changes and reused access tokens.
The qualified live route uses system WebKit WebRTC. The only allowed capture
permission is microphone access for the initial main frame at
https://miller.invalid/, after native authorization and the explicit Start
Live Voice action. Camera, subframe, foreign-origin, navigation, popup,
download, and unrelated permission requests are denied. The peer is local HTML
in an ephemeral data store, uses typed main-frame JavaScript calls rather than a
general message bridge, and is removed during cleanup. SDP is bounded in memory
for negotiation only and is not logged or persisted.
WebRTC tracks are the only primary media input and output. Direct GPT-Live's sideband supplies bounded lifecycle and transcript state; the retained App Server sideband does so only for the explicit fallback. Either may influence the speaking indicator but cannot enqueue audio for AVFoundation playback. The older AVFoundation PCM implementation remains isolated, non-default, unqualified groundwork.