Until tagged releases begin, only the latest main branch is supported with security fixes. Older commits and private forks may not receive fixes.
Use GitHub's Report a vulnerability / private security advisory flow for this repository if it is enabled. Include the affected commit, impact, minimal reproduction, and suggested mitigation, but remove credentials, real transcripts, private paths, hostnames, cookies, and user data.
Do not open a public issue for an exploitable vulnerability. If GitHub private vulnerability reporting is not available, open only a minimal public issue asking the maintainers to establish a private channel—do not include vulnerability details. Maintainers must confirm a durable private reporting channel before public release if GitHub advisories are not enabled.
No response or remediation SLA is promised for v0.1. Please allow maintainers time to investigate before disclosure.
Wayang is a privileged control interface for one trusted user and the pi coding-agent harness. Depending on configured tools and extensions, an agent can read/write files, execute commands, manage sessions/jobs/apps, and control an authenticated browser. Provider credentials, OAuth state, transcripts, projects, browser profiles, and application data may all be reachable from the host process.
Wayang:
- is not a general same-user sandbox;
- is not a multi-user or multi-tenant service;
- enforces registered Protected-project boundaries and agent allowlists for participating path tools and bash; ordinary restricted runtimes in Standard projects remain project-scoped, while a restricted runtime inside its authorized Protected project may read ordinary/unregistered host paths and Standard projects but may write only its own project except for mutation-denied project
.picontrol-plane files, and cannot read any other Protected project or protected backing artifact; the exact migration-seeded Wren profile in a Standard project—including scheduled runs—may read and write ordinary host paths and use visible Unix IPC while every Protected root remains masked; an exact reviewed project+profile assignment may instead grant direct host execution that bypasses those masks; none of these modes isolates temporary files, arbitrary same-UID processes, visible same-user services, or trusted in-process extensions from the host user; - deliberately allows every outbound TCP destination from sandboxed bash through HTTP/SOCKS proxies, including Internet, loopback, LAN, and VPN services; this is not a network isolation or data-loss-prevention boundary;
- removes bash when the required OS sandbox cannot prove its configured filesystem restrictions and, for socket-blocked profiles, Unix control-socket restrictions;
- does not make unreviewed pi extensions/packages safe;
- treats Project privacy mode as the cross-session transcript boundary: exact catalogued Standard-session transcripts and attachments are readable by other eligible interactive sessions through bounded tools or exact-file reads, regardless of target Project agent allowlists; Protected, quarantined, and unclassified session artifacts remain unreadable, all cross-session writes remain denied, and sandboxed bash retains no Pi/Wayang storage access. Catalog metadata parsing and
session_read/exact-file transcript classification share one authorizer: the path must be a canonical single-link regular file under a configured Pi session root, have one unique durable owner, match the bounded header ID/CWD, retain an exact-false quarantine marker and immutable Standard Project binding, and avoid every higher-priority universal deny. Duplicate mixed-privacy ownership and arbitrary canonical files fail closed. A previously unseen external Pi transcript may be staged from header metadata only when its CWD already identifies one currently authorized Standard Project; the durable denial-first row is committed before any body read, while unknown and Protected CWDs are never auto-imported; - does not provide TLS or certificate management;
- cannot make public exposure safe solely by requiring a shared password.
Authentication controls who can reach Wayang. Project policy narrows participating tool, filesystem, memory, and workflow actions; it does not reduce the authority of the main Wayang/pi process, trusted in-process extensions, unrelated same-UID processes, or network-enabled shell commands after login. A shell can forge an HTTP Origin and reach passwordless loopback APIs. Memory none/read prevents participating memory tools and direct filesystem mutation, but cannot prevent deliberate disclosure to a reachable Memoriki/MemPalace or other network service.
Sandboxed bash receives a strict functional environment allowlist rather than the backend's ambient provider keys, OAuth/AWS credentials, proxy credentials, loader hooks, or arbitrary deployment variables. This does not narrow wayang.host-execution.v1, which deliberately inherits the Wayang OS user's ambient authority. The Wayang checkout's launcher .env and .env.backup remain path-denied even when the checkout is not registered as a project. Project deregistration is refused while a project-local managed browser root exists so bearer-sensitive profile data does not become an unregistered ordinary path.
PIN-backed capability authority is identity-neutral. Its sole live durable authority is an approved association between one immutable Project ID and one stable Agent Profile ID. Project privacy mode, profile enabled state, and the project's exact-profile allow decision are rechecked. Provider/model are fluid runtime choices and never confer, narrow, or revoke capability authority. Names, prompts, and legacy environment keys do not confer capability authority; copying a profile creates a different stable ID and does not transfer associations. Separately, for compatibility with pre-policy Wayang, only the exact migration-seeded Wren stable ID plus its non-user-settable historical kind receives global resources and broad ordinary-host filesystem/IPC access in Standard projects, including scheduled runs. Renames preserve that exact row; copies and lookalikes do not acquire it. Protected project roots and protected backing artifacts remain masked in this Wren mode, which is distinct from capability-granted direct host execution.
wayang.host-execution.v1 is restricted to Standard projects. It bypasses the bash filesystem sandbox and runs cooperatively as the Wayang OS user. It does not itself elevate privileges, but it exposes every file, process, credential, capability, network service, memory store, Protected backing path, and pre-existing privilege mechanism available to that account. If Wayang is privileged, host execution is equally privileged.
wayang.standard-browser.v1 is restricted to Standard projects and wayang.protected-browser.v1 is restricted to Protected projects. Each grants a fresh interactive runtime broad browser control through backend-owned explicit tools: navigation, inspection, clicks, typing public non-secret text, bounded downloads, and any site action reachable through those tools. Standard-browser v1 additionally authorizes the exact Project-Agent association to enumerate and enter every current or future named Standard Browser Profile, retain session-owned tab workspaces across profiles, switch its session profile, and change its current Project's default for future or unassigned sessions. Named Standard profiles intentionally share cookies, storage, service workers, browser history, and remote-account effects; exact tab routing is not authenticated-state isolation. Full-browser/VNC control, when available, is cooperative and profile-wide rather than workspace-private; session tabs provide exact agent/owner CDP routing, not cross-workspace pixel isolation. Protected browser authority remains confined to its separate exact Project-Agent realm. Neither capability is vendor-specific, read-only, or limited to export controls. A mistaken or compromised agent may disclose page data or make consequential account changes. Login, MFA, CAPTCHA, payment, and other secret-bearing steps remain human-only and must never enter chat or tool input.
Interactive capability-browser downloads stage under private runtime storage using Chromium GUID names. A capability browser realm observes at most 32 downloads across runtime replacement, accepts at most 32 MiB per file, and keeps the publication directory at no more than 32 files and 64 MiB. Startup revalidates existing publications before accepting more. Only a completed, reauthorized, regular single-link file is exclusively copied into the Project under .wayang/browser-downloads/; unsafe, oversized, partial, racing, or unauthorized files are canceled or discarded. Wayang does not quarantine, scan, approve, open, import, or execute published files. Agents with project read/write access may inspect, move, or delete them; any separately granted execution surface may also execute them. Treat downloaded files according to their source and intended downstream parser rather than assuming Wayang has established content safety. Publication uses no-follow regular-file checks, exclusive destination creation, Project-bound canonical paths, and directory inode revalidation. As elsewhere in Wayang, this narrows cooperative races but is not isolation from a hostile same-UID process that can replace Project directories between checks.
Agent-visible browser status, snapshots, DOM summaries, selectors, links, accessibility metadata, and navigation results remove URL userinfo, query strings, and fragments. Full URLs remain backend-internal for top-level document attestation. Page text can still contain sensitive data; use browser tools only with a model/provider appropriate for the authenticated page.
Protected-automation downloads use a different bounded flow: Chromium stages at most 32 observed files per lease, each at most 32 MiB; a one-use handle atomically materializes a completed file under the run's incoming/ directory, capped at 32 files and 64 MiB total. This establishes completion, ownership, origin, and storage bounds—not content safety. Job code must validate expected formats and never execute downloaded content.
wayang.protected-automation.v1 has a completed Milestones 0–5 implementation and is compiled with activationAvailable: true. A deployment restart is needed only when putting new code into service; approval and state initialization do not require a restart. Capability approval reuses the command guard's existing identity PIN, and PIN entry is the normal human approval action for an exact reviewed Project–Agent pair. On startup, the deployed service automatically creates missing non-secret attempt/cooldown state under WAYANG_DATA_DIR with owner-only permissions and preserves it across reboots. Unsafe or missing PIN metadata and unsafe or malformed existing state fail capability approval closed without replacement. make setup-capability-approval is only an optional manual preflight/migration. Capability associations, deterministic jobs, and schedules persist in the service store. Until an exact association is approved, no production job, timer, browser realm, or credential-preparation path for that pair can run. Existing Protected Scheduled Agent Job denial is separate and remains unchanged.
For an exact eligible pair with PIN-approved authority, deterministic jobs run immutable Node snapshots through a shell-free Linux direct-Bubblewrap runner. Wayang constructs no Pi session or transcript and provides no model, provider, prompt, extension, MCP, shell, general executable view, Unix socket, or generic child TCP/UDP network. @anthropic-ai/sandbox-runtime remains a recorded NO-GO for this boundary because its shell/socat setup and compatibility writes do not satisfy it. macOS Protected automation is unavailable; there is no weaker runtime or host-execution fallback.
This is still a cooperative same-UID control, not hostile-code containment, DLP, or proof against daemonization and detached same-user processes. The child receives a read-only source snapshot but a writable mount of the whole authorized Protected Project, plus bounded private run and state roots. Completed or racing Project writes cannot be rolled back or hidden by cancellation/revocation. Project code and authenticated browser actions can disclose data or cause remote effects, and those effects cannot be undone.
Browser-enabled jobs receive only a bounded inherited RPC to a backend-owned, exact Project/Profile/Job Chromium realm—never raw CDP, cookies, storage, profile paths, screenshots, or arbitrary JavaScript. Stored navigation origins must be exact HTTPS origins. Wayang intercepts and attests top-level document requests, rejects disallowed top-level destinations and cross-origin redirect chains, and closes unexpected page targets. This interception is not a complete network allowlist: iframe documents and required page subresources are continued and may contact origins outside the job's top-level allowlist. Completed download source URLs must match an allowed exact origin before one-use handles can materialize bounded bytes into the run root.
Login, passwords, MFA, CAPTCHA, passkeys, and other secret-bearing steps remain human-only through an exact source-bound preparation viewer and the guarded credential broker; values must never enter chat, job source, argv, or tool input. Deterministic code reports a fixed needs_user outcome and exits rather than retrying ambiguous human or remote work. The persistent browser profile intentionally survives runs/preparation for reauthentication, but unlike snapshots, run state, diagnostics, incoming downloads, and run history, profile storage has no compiled byte quota and may grow on disk until PIN-confirmed job purge.
Explicit revocation, exact-profile allowlist exclusion, an incompatible project privacy change, or Project/Profile deletion tombstones the association denial-first. Profile definition edits—including instructions, tools, resource/memory modes, defaults, and disable/re-enable of the same stable ID—and project instruction/default edits preserve it by design. A disabled profile cannot run; re-enabling restores authority only through fresh runtime handles. Provider/model changes destroy stale runtime surfaces and rebuild them lazily under the same association without another PIN. Denial synchronously blocks future tool dispatch and terminates affected direct-host and Protected-automation process groups with bounded TERM/KILL. It cannot undo filesystem changes, browser actions, downloads, disclosures, credential use, or external side effects already completed. Use separate OS identities or stronger isolation when cross-project, memory, or browser-profile non-readability is required. make doctor checks prerequisites, not effective workspace associations.
Legacy identity-specific private environment keys may remain physically present because configuration updates preserve unknown keys, but current authorization ignores them. Do not inspect or rewrite .env merely to clean them up. make doctor checks capability approval authority metadata, not file contents or effective workspace assignments.
- Keep the default
127.0.0.1:8787bind whenever possible. - For remote access, use a trusted VPN/private network and HTTPS, and set
WAYANG_PUBLIC_ORIGINto the exact browser-facing HTTPS origin. Compiled loopback origins remain available for direct/SSH-tunneled administration. Remote passwordless owner controls are denied unless the explicit trusted-proxy identity bridge is configured. Consider both built-in shared-password login and an authenticated reverse proxy according to your risk. - A proxy must protect and forward every path and WebSocket upgrade, including frontend assets,
/api/*,/ws/*, app proxy routes, chat, browser CDP, and browser VNC. Block direct access to the backend that bypasses the proxy. - Treat every person/device with network access and credentials as able to control a host-level agent. VPN membership is not authorization isolation.
- Use
WAYANG_TRUST_PROXY=loopbackonly when the reverse proxy connects from loopback. Configure it to set the upstreamHostheader to the exactWAYANG_PUBLIC_ORIGINauthority and to replace—not append to or preserve—client-suppliedForwardedandX-Forwarded-*headers.X-Forwarded-Hostis never used for authorization. There is no broad forwarded-header trust mode. - If
WAYANG_AUTH_PROXY_IDENTITY_HEADERis enabled, the proxy must authenticate every Wayang path, strip every client-supplied value for that header, inject exactly one stable authenticated subject ID, and block direct backend access. Wayang accepts this identity only from a loopback peer at the exact configured remote HTTPS origin. Misconfigured header sanitization is an administrator-authentication bypass. - Use Secure cookies over HTTPS. Never disable Secure-cookie behavior for remote login. The optional
make local-httpscommand is a foreground Caddy/built-in-auth reference path for a private LAN; it does not install Caddy, configure trust/DNS, expose the loopback backend, or create a service. Review docs/local-https.md before using it. - Run Wayang as an unprivileged dedicated user when practical. Do not run it as root. This is especially important for any runtime granted host execution because it inherits the Wayang process's full OS authority.
- Review third-party pi packages/extensions before installation; they execute with the host user's authority. Preserve pi's project-trust gate and approve project
.piresources only after source review.
When built-in authentication is enabled, /healthz, login/static assets, GET /api/auth/status, and POST /api/auth/login remain public by design. Other APIs and all WebSocket transports must share the same session checks. Browser origin checks for state-changing requests and every WebSocket upgrade apply even when password authentication is disabled. Report any bypass privately.
Protect at least:
- root
.envand.env.backup; ~/.pi/agent/auth.json, settings, extensions, and session JSONL; exact catalogued Standard JSONL is intentionally cross-session readable inside Wayang, while Protected, quarantined, unknown, auth, and configuration artifacts remain denied;- the command-guard identity PIN under the XDG config root (normally
~/.config/pi/command-guard-identity-pin); WAYANG_DATA_DIR/workspace-capability-approval/pin-attempt-state.json, the private non-secret capability-approval attempt/cooldown record, created automatically with owner-only permissions when missing and preserved across reboots;~/.wayang/store.json,search.db,auth-sessions.json, private policy projections, and session-scopedattachments/; Standard-session attachments are intentionally readable by other eligible Wayang sessions but remain private at the OS/storage boundary, while Protected/unclassified attachment subtrees remain cross-session private;- configured file-audio Wren capsule, shared task, neutral adapter, response schema, Sol synthesis prompt, and disposable
WAYANG_DATA_DIR/audio-experiment/workspaces; - Protected-automation job/run metadata, immutable snapshots, bounded state/diagnostics/download staging, and persistent job browser realms under
WAYANG_DATA_DIR/protected-automation/; - projects and files Wayang can access;
- shared
WAYANG_DATA_DIR/browser-workbench/and explicit project.pi/browser-workbench/profiles, cookies, downloads, and artifacts; - the ephemeral browser-credential unlock socket and in-memory Bitwarden session;
- proxy/VPN configuration and logs;
- screenshots, debug output, browser traces, database copies, and backups.
The configuration wizard writes .env, its backup, and generated authentication material with mode 0600. It stores only a salted scrypt password record, not the shared password. Browser sessions use opaque HttpOnly, SameSite cookies; stored session records contain keyed token hashes. Rotate provider credentials and the Wayang password/session secret if exposure is suspected.
Do not attach .env, auth files, transcripts, profiles, traces, or raw logs to issues. Before sharing diagnostics, reproduce with a synthetic HOME, pi directory, Wayang data directory, project, and credentials.
The managed browser may contain active sessions to unrelated services. A compromised Wayang session can potentially act through that browser. Use a dedicated profile, minimize logged-in accounts, and keep profile directories private. The default ordinary-browser profile is shared across Wayang projects, so ordinary project boundaries do not isolate authenticated browser state. A Protected project is denied browser authority by default; associating wayang.protected-browser.v1 with an exact Project-Agent pair gives any model currently implementing that agent the broad browser authority described above, not a narrowed read/export surface. Protected persistence remains isolated by immutable Project and Agent Profile IDs.
Guarded Bitwarden fills are designed to prevent accidental credential values from entering UI state, agent results, persisted metadata, logs, or the clipboard. Credential choices are one-use and exact-origin/document-bound. Successful fill keeps user mode and blocks all agent inspection; sequential fills in the same top-level document union known redaction values. Only the exact-Origin UI-only allow route can enable read-only text/DOM inspection. Agent-visible outputs are redacted against raw known values and deterministic reversible URI/component, URL-form, standard-base64, and base64url representations; percent-hex matching is case-tolerant. Screenshots and every agent mutation remain blocked until a confirmed new CDP main-frame loader/document identity. URL-only changes (pushState, replaceState, hash) and failed/uncommitted navigation do not clear protection. General Resume Agent cannot bypass that state. Wayang does not click Submit or explicitly submit a form, but sites may react to the input/change events emitted by fill.
This is not a strong boundary against another process running as the same OS user, which may be able to inspect Wayang/Chromium process memory or attach to CDP. A same-UID process that also has the normal UI session can send an untagged exact-Origin request that is classified as UI traffic; this is an explicitly accepted guarded-boundary limitation, not a hard-isolation claim. Unlock only from the provided local-terminal helper; never pass a master password or session key through chat, command arguments, HTTP, or environment configuration.
Project-local apps are executable code managed by the backend and displayed in the trusted Wayang origin. Review app manifests and source before launching them. Do not treat an iframe as a security sandbox for hostile code. Agent Apps requests use a loopback, route-scoped, in-process capability bound to the exact source Wayang session; central policy reauthorizes that source profile against the target project on every operation, and a forgeable loopback Origin is not agent authorization. Registration, listing, state, logs/events, and stop cannot launch app code. Because manifest commands remain unsandboxed same-user shell commands, agent start/restart fails closed whenever any protected project is registered; authenticated manual launch remains available after human review. Managed app children and proxy upstreams do not receive Wayang's internal capabilities, but app processes still run as the Wayang OS user.
Project-root AGENTS.md writes use optimistic hashes, no-follow reopening, inode/content revalidation, registered-root revalidation, and atomic no-overwrite creation. These controls reject stale/cooperative races; they do not isolate Wayang from a hostile same-UID writer, which can race after the final check or modify the file after commit.
Catalog body parsing admits no more files concurrently than the configured worker count. Workers—not the main event loop—reopen authorized files no-follow, revalidate their fingerprints, and reject bodies over 16 MiB before allocation; the catalog does not create whole-file main-thread buffers or a whole-corpus body-task Promise.all.
session_read returns at most 200 selected lines and 48 KiB, and processes at most 256 KiB total per call including any prefix skipped to reach a requested line offset. Responses report scanned_bytes, scan_byte_limit, and scan_limited; a high line offset that cannot be reached within the ceiling returns no continuation offset rather than scanning the transcript synchronously without bound. Canonical no-follow file checks and the target privacy/path recheck still apply.
Exact external-action approvals are an optional bridge for separately reviewed connector extensions. Connected-browser presence, an authenticated chat WebSocket, and the client-selected session generation are delivery/race signals—not proof of a human. Approval requires the existing owner-only command-guard identity PIN through Wayang's hardened persistent attempt/cooldown authority; denial is PIN-free. The PIN is transient, never enters the approval request, terminal event, acknowledgement, chat history, or connector metadata, and a wrong or malformed value consumes the attempt and denies that exact action. Quarantined sessions never count as approval clients. Missing/unsafe PIN metadata, cooldown-state failure, expiry, disconnect ambiguity, or identity mismatch cannot approve. Display metadata rejects unsafe Unicode formatting and control characters, including bidi overrides/isolates, rather than rendering a spoofable preview.
The bridge binds its decision to the exact session, request ID, and extension-supplied argument hash, and reports explicit terminal/stale/unknown UI states. It cannot verify that a connector extension truthfully summarized its raw arguments, enforce that the extension uses the displayed hash, or stop trusted in-process code from bypassing the bridge. Review the companion extension and require it to deny execution unless the returned decision exactly matches the paused call. An agent or same-user process that learns the identity PIN can still approve; keep the PIN outside chat, browser automation, logs, and connector code.
The optional restricted-profile MCP proxy is a backend-owned positive allowlist, not the global Pi MCP adapter. It binds one private mode-0600 policy to an exact live session/profile/Protected project and only compiled reviewed server/tool ceilings. Children receive a strict environment, bounded stdio framing, sanitized errors, and teardown on revocation; protected oversized results never intentionally use shared temporary storage. Review launcher source and require fixed paths plus exec. The boundary does not stop arbitrary same-UID launcher replacement, a deliberately network-enabled bash command from contacting reachable services, or all pathname races against a cooperating same-UID writer. Use separate OS isolation and service authentication where those threats matter.
Configured TTS sends assistant text to the selected service. A remote broker/provider is a separate disclosure boundary and must be secured independently.
The comparative file-audio experiment is disabled by default. When deliberately enabled, it is restricted to the exact migration-seeded Wren profile in a Standard interactive session. Backend-issued source-session attachment IDs and process-local same-current-user-turn preview permits bind the complete topology, attachment digest, runtime generation, project/profile, and provider/model. Permits are short-lived, single-use, and lost on restart; execute and revoke must each prove the exact originating current turn. Execute reopens the upload no-follow and revalidates inode, size, and SHA-256 before any media, adapter, or DSP call. Replays, scheduled/non-browser continuations, stale or changed turns, other sessions, model/profile/runtime changes, and changed files fail closed. Preview accepts only declared MP3/RIFF-WAVE types, is not a questionnaire, and performs no prompt/capsule/key/file read, process launch, DSP operation, or provider call.
A valid execute always runs the complete A/B/C topology; callers cannot select a subset. It structurally validates actual MP3/WAVE bytes, then runs canonical ffmpeg/ffprobe through a shell-free Linux prlimit → Bubblewrap chain with explicit native resource ceilings, no host network, dropped capabilities, minimal read-only runtime mounts, isolated /tmp, and only the exact private media workspace writable. It sanitizes once in that disposable storage and gives A, B, and deterministic local DSP the same exact sanitized buffer. Five distinct absolute owner-private no-follow artifacts are frozen by configured SHA-256. Before either direct-audio provider use, the Wren capsule, neutral adapter, byte-identical shared task, and response schema are loaded and hash-checked. A receives the Wren capsule as developer instructions; B receives the neutral adapter as developer instructions. Each receives the shared task, response schema, and audio as separate user content. OPENAI_API_KEY is resolved only immediately before each call. Both use fixed gpt-audio-1.5, the official HTTPS Chat Completions endpoint, text-only output, and store: false, and each response must pass the strict direct-response validator.
After A and B validate, deterministic local DSP generates bounded numeric metadata plus exactly three PNG artifacts. DSP is an artifact source for synthesis, not the outer model's Arm C response. Two distinct cryptographically random 128-bit lowercase-hex labels blind A/B and candidate order is randomized. Only then are the Sol prompt and response schema re-opened and hash-checked, and only then is the key resolved for the isolated fixed gpt-5.6-sol Chat Completions adapter. That adapter receives only its private developer instructions and schema, the two already-validated direct responses under opaque labels, bounded DSP numeric text, and the three PNG byte artifacts. Its output must pass strict synthesis validation for arms A/B/C and the 64-character contiguous/whitespace-normalized private-prompt echo guard.
The ordinary outer session is not the synthesizer. It deliberately receives the two validated direct provider outputs under opaque labels, the validated synthesis response, bounded synthesis-provider metadata, and nonbinary DSP metadata/digests. This direct provider-output release is part of the accepted experiment design. Candidate response IDs and token usage are withheld as potential arm-mapping side channels. Before reveal it receives no arm-to-label mapping, A/B implementation identifiers, private prompt or schema, raw audio/base64, host path, API key, or DSP PNG/image block. Prompt-echo checks and public-output filtering are defense-in-depth against accidental disclosure, not a blocking confidentiality claim: transformed, inferred, or deliberately fragmented output can evade them.
Preview and execute are both model-callable. Clemente has accepted same-current-user-turn model-call consent for this experiment: the binding prevents execute authority from crossing turns but is not a separate human click or PIN approval. Deployments requiring deterministic per-run human approval must add a UI-owned, model-inaccessible one-use authorization.
After each successful complete execute, the exact live runtime retains a cooperative process-local blind record and returns a fresh random 128-bit lowercase-hex run_id. At most eight records live for 60 minutes or until earlier runtime close/replacement; service restart loses all records. Each record binds the exact source attachment/hash, opaque labels, frozen private label-to-arm mapping, creation/expiry, optional immutable score/commitment, and reveal state. Oldest-first eviction and close remove mapping references. Unknown, expired, evicted, stale-runtime, and closed-runtime IDs fail closed.
Reveal is model-callable but unavailable until the outer model finishes its blind comparison and submits one exact score covering both labels. The runtime canonicalizes and freezes the first valid submission and returns its SHA-256 commitment without the mapping; later browser turns are allowed only inside the same still-current runtime. Scores are one-shot and cannot be replaced. Reveal returns the committed preference and condition guesses alongside the exact label-to-public-condition mapping (bounded_wren or neutral_specialist) and is idempotent while the record remains live. This is a cooperative score-before-reveal protocol, not durable audit storage or protection against a model intentionally guessing/inferencing the mapping before reveal. Startup installs inert closures even while disabled, and startup/preview never reads prompts, keys, audio bytes, or synthesis artifacts.
Use committed lockfiles and npm ci. Review dependency advisories for actual reachability rather than suppressing or blindly accepting them. Update from a reviewed source, rerun make check and relevant E2E/auth tests, and back up private data before migrations.