IRIN is a local-first, single-operator product: structured multi-model deliberation delivered as a signed macOS app (with a browser War Room from source on macOS and Ubuntu), an optional governed Gateway, and a separate Sentinel/Watch lane. Everything binds to 127.0.0.1 by default. Direct provider transport is the default; Gateway is explicit per-seat opt-in. The Watch producer and dispatcher are off by default. It is experimental and makes no compliance, certification, or conformance claims of any kind.
Default-off, localhost stance
All network surfaces default to loopback. Council, War Room Web, and Gateway bind loopback in the canonical runtime. Compose publishes Gateway only as 127.0.0.1:18080:8080 (demo equivalents on 28080). The watch-plane components that turn observation into pending escalations are disabled at startup: WATCH_DISPATCHER_ENABLED=${WATCH_DISPATCHER_ENABLED:-false} and WATCH_PRODUCER_ENABLED=${WATCH_PRODUCER_ENABLED:-false}.
Loading or enabling a Sentinel is not arming. Arming the Gateway Watch producer is a ceremony, not a flag flip. It requires dual custody (host bearer token list GW_ARM_PRINCIPALS plus hardware-backed attest) and still performs writer-claim checks and appends to arm_audit. After any live proof the loop flags are reset to default-OFF. The interactive path is a hardware ceremony (Secure Enclave / FIDO2 tap); a boot env path exists that is an audited config flip — it can start watchers but cannot claim, spend, or sign a new directive without a hardware-attested arm.
Direct vs Gateway
Council calls provider APIs and authenticated local CLIs directly unless a seat is explicitly set to Governed via Gateway. Gateway adds metering, budget limits, and a signed audit-ledger record for that call. Spend limits on Gateway paths do not constrain Direct calls. Outside Gateway, provider spend is unconstrained by IRIN.
The signed IRIN.app
The product ships as a signed, notarized IRIN_<version>_aarch64.dmg on GitHub Releases (Apple silicon only). Verify the download against the HASHES.txt attached to the same release. The core app is Docker-free and keeps Gateway off by default; a missing Docker installation is not an error for Direct mode.
The optional Gateway pack is app-owned (irin-desktop-gateway) and starts only from Settings → Enable Gateway. Its images are pinned by digest in the installed manifest. The Council client key (GW_API_KEY) and AUTH_PEPPER live only in the login Keychain, device-local — never in files, logs, or the renderer — and governed proceedings begin only after authenticated readiness. The pack never touches a source checkout's Gateway project or volumes.
Touch ID arming is authorization, not a setting
IRIN.app runs the Watch-producer arm ceremony from Settings, beside the Gateway control: enroll a hardware credential, rehearse, then arm with a biometric tap. Arming authorizes the producer to cause paid Council deliberation and signed Outbox directives — it is not a convenience toggle. Enabling Gateway arms nothing: the default credential registry loads empty. Enrolling is not arming. The producer stays off at boot until the ceremony arms it. The ceremony does not enable the dispatcher; that is a separate gate.
Spend fails closed unless a hardware ceremony has written a signed active_arm row. Any authorized principal can disarm immediately, with no second factor. Hard ceilings apply on the armed path: a $50/UTC-day Watch spend maximum the operator may only lower, a per-event fanout reserve, and a signed spend window. The enrollment registry holds public credential records only. It is mounted into the validating sidecar, never the edge.
The control tells the truth about its own state: "Touch ID ready" when enrolled and disarmed, a distinct "Rehearsal passed — not armed" after a rehearsal (the same stage, challenge, signature, and audit path, with the producer still off), and "Armed until" with the lease deadline while armed.
Signed directives (Ed25519 over RFC 8785 JCS)
On the Watch→Outbox lane, Council emits a Directive naming job, scope, stop condition, and return expectation. Gateway serializes it with the sovereign-protocol RFC 8785 JCS canonicalizer, signs the canonical bytes with its Ed25519 private key, and writes both the canonical JSON and the signature into a row in directive_outbox. Any later reader can recompute the JCS form and verify the signature against the public key. An ordinary War Room run does not create this row.
Ed25519 + JCS provides verifiability (deterministic canonical form independent of encoder whitespace or ordering) and custody (the signed row is the durable record that the decision crossed the deliberation boundary under the operator's key).
Three distinct signed surfaces
watch_fires carries the hash chain (SHA-256) so tampering with prior fire rows is detectable. directive_outbox stores individually signed Directive envelopes (per-row Ed25519) — not a hash-chained table. The governed Gateway audit ledger is a separate signed ledger for routing, accounting, and decontaminator events on governed paths. Do not conflate these three.
The signing public key is served without auth at /watch/outbox/pubkey; outbox row reads and all mutations require an authenticated key (unauthenticated requests get 401).
Budget caps
The ≤120 s budget is the closed-loop triage envelope only (WATCH_DISPATCHER_COUNCIL_TIMEOUT_SECS default). Interactive War Room and bare CLI sessions run on the operator's clock. On Gateway-governed paths, spend is metered with atomic reserve-settle-or-rollback; daily caps and per-directive ceilings act as hard ceilings enforced before spend.
- No SOC 2, ISO/IEC 42001, NIST conformance, CISA/NSA validation, EU AI Act compliance, or any certification.
- No multi-tenant isolation guarantees (operation is single-operator and local-first).
- No claim that model output is correct or that a Directive will be executed.
- No claim that every call crosses Gateway, or that every action is recorded or executed.
- No claim that the surrounding deployment is safe for public or LAN exposure.
- Host compromise (root or same-uid) can read on-disk signing materials and forge verifiable rows on surfaces whose private key material is present. Hash chains provide tamper-evidence, not prevention.
- No autonomous operator-ready Worker ships. Authenticated worker-management routes may be mounted; the built-in worker loop is disabled by default and is not an operator-ready executor. Product guidance ends at the signed Outbox directive.
- Ephemeral workers and a tenant librarian are not current IRIN product primitives.
- Critical-infrastructure-grade assurance is out of scope. Protection from host compromise is not provided.
Acknowledged gaps (today)
Memory & context poisoning: The precedent store and other retrieved context are not yet watched by integrity sentinels. Hash chains protect the fire log; outbox rows are individually signed; neither protects model context.
Sequence-level / multi-step attacks: Single-step policy, fences, and budgets exist on governed paths. There is no stateful sequence watcher over chains of actions yet.
Reporting a vulnerability
Please do not report security vulnerabilities through public GitHub issues.
Report privately to [email protected] or via GitHub Security Advisories ("Report a vulnerability" under the Security tab on the repository).
Include a description of the vulnerability, steps to reproduce, and any relevant logs, ledger/verify output, or traces. Coordinated disclosure is expected.