SectionCurrent referenceStatuslivingLast reviewed2026-10-04

Home runtime identity profile — decision record for the first personal-domain vertical slice

Companion to: N4A_DEVICE_AUTHORITY_EVALUATION.md (the primitive), IDENTITY_SEMANTICS.md (the canonical contract), GEN_SUBJECT_CONTEXT_GENESIS.md (genesis) · Issues: #2599 (N4), #2602 (GEN), #2694 (ladder), #2800 (N1-D), #2801 (naming), #2806 (ADR-0083 drift) · PR: #2807

Truth status. This is a decision record and consumption profile, not a new identity model and not an implementation claim. Every primitive it names is classified below as PRODUCTION / LIB-TESTED / FIXTURE-ONLY / EXPERIMENTAL / DOC-ONLY / MISSING against the live checkout at main 7ef8a670a plus PR #2807 (2026-10-04). Rows (f) and §6.3 were updated on 2026-10-24 after N1-D (#2800) and its daemon rehydration (#2843) merged; every other class is as of the original snapshot. Where a class says MISSING, nothing downstream may build a substitute. The motivating deployment (a thin client to a hosted workstation) is a fixture for requirements; it is not part of ICN and no real person's identity material exists or may be created from this document.


0. The question and the one-line answer

Which ICN primitives does a person's Home runtime consume so that a phone, a small desk computer, a PC and a hosted workstation can be replaceable device Principals under one durable human, in several contexts, without any device, host or operator becoming the human?

Answer. The human is an N1 Subject per context (GEN-A inception, client-held ContinuityRoot); each device is an ordinary Principal holding a per-context key of its own; authority is an N1 Authorize grant in the Subject's log, bounded by capability set and optional position span; revocation is an N1 Revoke; a relying party decides any act with N4-A verify_device_act at a position it chose. All of that is LIB-TESTED today. What is MISSING is the enrollment ceremony that moves a grant request from a new device to the phone and the event back (N4), a transport container for facts and acts (next slice), a GEN context kind for personal and household contexts (#2602), and recovery (N7). The Home runtime consumes; it never defines.

On naming: per #2801, Mutualware is the intended public name of ICN itself, public-brand-first and protocol-stable. There is no "Mutualware identity" and no "contract layer" above ICN. A downstream document that frames a Home runtime as a contract layer should read it as a consumption profile of ICN identity — this document — and nothing more.


1. Primitive map (acceptance item 1)

Classes: PRODUCTION exercised by a running node, route or deployment (operationally wired; ADR-0032/0033 maturity claims need that evidence) · CLI-REACHABLE executable by an operator from a workspace binary, with end-to-end tests, but wired into no runtime, route, deployment or profile · LIB-TESTED implemented and tested, no runtime caller · FIXTURE-ONLY exists only inside tests · EXPERIMENTAL present but unreachable, stubbed or unsafe · DOC-ONLY · MISSING.

Responsibility Primitive Class Canonical owner Evidence
(a) the human Subject, per context authority_log::SubjectId = inception event id; GEN-A incept_subject_context_v1 LIB-TESTED IDENTITY_SEMANTICS.md §2.2; GEN_SUBJECT_CONTEXT_GENESIS.md subject_context.rs; tests/gen_subject_context.rs + Python reference; zero runtime callers
(a′) continuity across contexts ContinuityRoot (secret + plan + per-context nonce), client-held only LIB-TESTED (construction), DOC-ONLY (custody) IS §2.5, §10; HIA §12.2 authority_log/construct.rs:82; custody rule R1.2 [HARD] HIA L202
(b) device Principals under the Subject PrincipalKey; an Authorize body naming the device LIB-TESTED IS §2.7 body.rs AuthorizeBody; construct::authorize_event
(b′) per-context device keys deterministic per-context key from the device's own secret DOC-ONLY HIA §11.1 L1614–1616 no derivation helper exists in code; client-side, trivial, and must not take SubjectId as input (HIA L1618)
(c) bounded delegation CapabilitySet {Sign, Encrypt, Present, Recover} + Option<ValiditySpan>; N4-A evaluation LIB-TESTED IS §2.7 (attenuation mandatory, I5); N4-A doc device_authority.rs; 25 tests (#2807). No caveat/resource field: richer scope is #2599
(c′) relying-party verification of a device act verify_device_act at a relying-party position LIB-TESTED N4-A doc §7 tests/device_authority.rs
(d) revocation N1 Revoke + derive_prefix LIB-TESTED (semantics); MISSING (propagation/effectiveness) IS §2.7; #2599 invariant 5; N3 #2598 derive.rs:304 removes the grant; prefix evaluation makes "at E" answerable
(e) recovery N1 pre-rotation Rotate/Recover establishment LIB-TESTED (establishment transitions); MISSING (guardian/threshold protocol, backup) IS §2.5; N7 #2603 construct::establish; Recover clears device grants (derive.rs:288)
(e′) legacy recovery recovery.rs, RPC recovery.*, SDIS recovery routes EXPERIMENTAL — unsafe: node key signs the trustee attestation; SDIS complete is a stub HIA F13; #2591, #2448 icn-rpc/src/handler/recovery.rs:138; api/sdis/recovery.rs:306
(f) durable local facts AuthorityFactStore + sled adapter (N1-D) LIB-TESTED; merged in #2800, rehydrated fail-closed at icnd startup since #2843 #2799/#2694; ADR-0087, ADR-0088 record = event_id ‖ signature → canonical body; no runtime producer writes facts
(g) external facts+act container EvidenceBundle (icn.n4.evidence-bundle), verify_evidence_bundle (N4-B, converged 2026-10-04) LIB-TESTED (25 tests observed 2026-10-27 + an independent Python reference) N4B_PORTABLE_EVIDENCE_BUNDLE.md evidence_bundle.rs; fact record = N1-D layout
(h) stateless CLI consumer icnctl device-authority verify|inspect CLI-REACHABLE (5 end-to-end tests observed on icn-dev) — a relying party, never an authority; not PRODUCTION: no runtime, route or deployment exercises it N4-B §9 bins/icnctl/src/device_authority.rs
(h′) enrollment request and attenuated approval EnrollmentRequestV1, approve_enrollment (N4-C) LIB-TESTED (11 tests observed 2026-10-27; ceremony end to end in a fixture) N4C_DEVICE_ENROLLMENT_REQUEST.md device_enrollment.rs; transport, UI, delivery not built
(m) device ↔ transport-channel binding ChannelBindingV1 as the payload of a Present DeviceActV1 (N4-D) LIB-TESTED (16 tests observed 2026-10-27; a connection is admitted only through N4-E current admission, admit_channel_binding; verify_channel_binding_at is the separate historical question); no runtime calls it N4D_DEVICE_CHANNEL_BINDING.md device_channel_binding.rs; node BindingInfo not reused
(n) current admission position (class 2) admission_position, verify_device_act_at_admission (N4-E) LIB-TESTED (9 tests observed 2026-10-27) N4E_CURRENT_ADMISSION.md device_admission.rs; position from the relying party's own facts, never the act
(h″) the two-device acceptance invariant through every boundary tests/device_lifecycle_acceptance.rs LIB-TESTED (2 tests observed on icn-dev) this document §2 request → approval → facts → bundle bytes → verdict; A revoked, B accepted, S unchanged; order-independent; stale facts fail closed
(i) context kinds GEN-A GovernanceDomainV1 only LIB-TESTED (one kind); MISSING (personal/household) GEN doc §5; #2602 subject_context.rs:180
(j) legacy device path multi_device.rs, /v1/devices/* EXPERIMENTAL — unreachable: no DID document is ever created HIA F8; #2588/#2590; superseded by N4 identity_mgr.rs:156
(k) sessions gateway HS256 JWT + jti revocation PRODUCTION — not identity AUTHORITY_SPINE.md a session proves a request may enter a handler; never authorship
(l) node transport identity DID-TLS BindingInfo in Hello PRODUCTION — node Principal only IS §9; network-session-identity-binding.md bundle.rs:181; handlers/hello.rs

What a Home runtime consumes today, truthfully: rows (a), (b), (c), (c′), (d), (g) and (h′) as a Rust library, and row (h) as a stateless icnctl verb. Row (i) (context kinds) and the transport/delivery around (h′) are the seam it is still waiting on.


2. The fixture test (acceptance item 2) — done

icn/crates/icn-identity/tests/device_authority.rs (PR #2807): Subject S; A authorized at 1, B at 2, A revoked at 3. A's act at E = 3 → Refused(DeviceNotAuthorized); B's → evidence {granted_at: 2, generation: 0}; S's identifier, authority set, generation and next commitment identical before and after; A ∉ devices, B ∈ devices, B ∉ authority; device bytes ≠ Subject bytes. A's act at E = 2 still verifies. 25 tests; independent reference 4/4.

Extended through every boundary (2026-10-04): tests/device_lifecycle_acceptance.rs enrols A and B through N4-C requests and attenuated approvals, carries the facts and each act as N4-B bytes to an independent relying party, revokes A, and pins: A refused and B accepted at the current position; S's identifier, authority set, generation (still 0: nothing rotates) and pre-rotation commitment unchanged; the same verdicts for every arrival order of the facts; and a relying party that evaluates at a position it does not hold failing closed (PrefixIncomplete). A relying party whose facts stop before the revocation still accepts A at its own older tip: it is behind, not deceived (N4-E §4). 2 tests observed 2026-10-27.


3. The external verification boundary (acceptance item 3)

Form Status
library API icn_identity::device_authority LIB-TESTED
documented canonical act bytes + cross-implementation vectors done — N4-A doc §6, §8
deterministic container for (N1 facts, act) that a non-Rust client can carry built — N4-B (EvidenceBundle, icn.n4.evidence-bundle; 25 tests; independent Python reference)
stateless icnctl verb consuming that container, no icnd built — icnctl device-authority verify|inspect (exit 0/1/2 = authorized/refused/malformed; 5 end-to-end tests)
gateway route deliberately not planned as an authority; at most a hosted relying party (N4-A §9.2)

The container's record layout should align with #2800's event_id ‖ signature → canonical body so that "what the daemon persists" and "what a client carries" are one framing. With N4-B landed, external systems consume ICN truth reaches a stateless CLI relying party; what the Home runtime still lacks is the ceremony transport (N4-C defines the request and approval, not their carriage) and a context kind for personal/household contexts (#2602).


4. GEN stance for Private / Household / a person's own project (acceptance item 4)

Can a personal or household context exist today without a GovernanceDomain?

Mechanically, yes: GEN-A accepts any non-empty UTF-8 context_id and never checks that a GovernanceDomain exists (subject_context.rs:293–305). Semantically, no: the only kind tag is GovernanceDomainV1 (0x01), so a Subject incepted for "household.example" is a Subject in a governance domain called household.example. That is the institutional collapse the contract leaves to GEN to avoid (IS §2.2: "a context may later be bilateral, informal, community-level … GEN defines").

Three routes were weighed:

Route Verdict Why
Individual-owned InstitutionalDomain (ADR-0083 L88–92 admits Individual as an owning entity class) rejected for personal/household use It makes a person an owning entity class and so extends the person-as-Entity debt IS §8 L538–544 dispositions for retirement. Declaration is mandate-gated and reached through a governance:write bearer (PRINCIPAL_MODEL §2.8), i.e. a registrar in the human's genesis path, against R1.1 [HARD]. A household is not a tiny cooperative and must not need a charter, a steward or a GovernanceDomain row to exist.
GovernanceDomainV1 with a household/personal string rejected Correct bytes, wrong meaning; nothing could later distinguish a household from a domain; no object exists for members to recognize each other against.
New GEN context kinds PROPOSED — route to #2602 The kind tag is inside every GEN preimage, N1 is unchanged, and N4-A evaluates identically whatever the kind. The gap is GEN's alone.

Proposed shape, for #2602 to decide (not decided here):

NON-NORMATIVE until #2602 decides: neither kind exists, nothing may implement or rely on them, and the kind byte would feed the N1 KDF permanently.

  • PersonalContextV1 — one human, no other party. context_id is an opaque 32-byte descriptor digest the person's own client mints and keeps; it is never published beyond the person's own clients and devices. No registrar, no founder, no charter. Private and a person's own small project are both this kind; if a project later takes on members it becomes a household or an institution through genesis, not by relabelling.
  • HouseholdContextV1 — two or more humans, no governance object. context_id is the genesis digest of a household descriptor the founding members' clients agree on; membership is mutual recognition between members' Subjects (the GEN-B recognition shape, bilateral), never an AuthorityGrant, never a steward. A household has no treasury, no charter and no office; if it ever needs institutional authority it undergoes institutional genesis and becomes an Institution — it is not promoted in place.

Each human still holds one Subject per context, so person-A-in-Household and person-B-in-Household are two Subjects, unlinkable to their Private Subjects except through each person's own ContinuityRoot.

What this means for the vertical slice now. The device-authority proof is context-agnostic, so it is testable today against a fixture GovernanceDomainV1 context. No production Private or Household context may be created until a GEN kind exists; the deployment track is not blocked by that, because it does not create any context.


5. Contradictions, settled against the text

Contradiction Resolution Basis
per-context device keys vs one key per endpoint per-context. A device holds one key per (device, context); the same key in two contexts links the person's Subjects to any observer that sees both (a Principal is "fully correlatable across contexts by construction", IS §2.1). The Pi, with three contexts, holds three device Principals derived from one local device secret. The derivation is client-side and must not take SubjectId as input. "One key per endpoint" stays withdrawn. HIA §11.1 L1614–1620; IS §2.1
where the ContinuityRoot may live vs a VM-hosted Home on the person's own client — the phone is the authority edge — with pre-rotation material backed up off-device at onboarding (HIA §12.2 default (a)). A VM-hosted Home runtime holds public N1 facts and the VM's own device keys; it is a device Principal (the "hosted/client device"), not a custodian. A Home process may hold the root only if the person has chosen that process as their own client, which makes its operator a custodian of the person's identity; the default is refuse, and a host operated for someone else never qualifies. HIA R1.2 [HARD] L202–203, §12.2 L1741–1745; IS §5 L337–339, §10
AuthorityGrant grantor must be chartered vs household authority two different objects. An N1 DeviceGrant is a fact in a Subject's log and needs no entity, charter or grantor beyond the Subject's own establishment authority (IS §2.7). ADR-0014's AuthorityGrant is institutional authority and its grantor rule is correct for institutions. A household mints no AuthorityGrant; its members delegate to their own devices in their own logs. Household shared decisions are recognition and app-layer policy, not grants. ADR-0014 L191–196; IS §2.7, §8
Mutualware naming vs a network-ops "contract layer" Mutualware = ICN's public name; no layer. Rename the framing to "Home runtime consumption profile of ICN"; protocol identifiers (icn-*, did:icn:, domains) do not change. #2801; PR #2802 claim ledger C-ID-01 / C-BRAND-01

6. What a Home runtime may implement against today (acceptance item 5)

This is the specification a deployment track may build to without inventing identity. Every step says which ICN object it produces or consumes and whether that object exists. Steps marked MISSING may be designed around but must not be substituted by a conventional mechanism that pretends to be the ICN one.

6.1 First boot of a device (any endpoint: desk computer, PC, hosted client)

Step Produces Class Rule
generate a device secret locally, hardware-backed where available nothing ICN-visible — never leaves the device; never a human key
derive one device Principal per context from that secret and the context descriptor PrincipalKey (32-byte Ed25519) LIB-TESTED (PrincipalKey::try_from_bytes); DOC-ONLY (derivation) no SubjectId in the derivation
present to the authority edge a self-signed EnrollmentRequestV1: the Subject it asks to join, its per-context Principal, the capabilities it asks for (never Recover), a label, a nonce SignedEnrollmentRequest LIB-TESTED (N4-C) possession of the key, never authority; carriage of the bytes is MISSING

The device presents a key, nothing more. Possession of the device proves nothing about the human.

6.2 Enrollment approval

Step Produces Class Rule
the authority edge (the person's phone, holding the context's establishment key) decides — — a human decision on the person's own client
it authors an N1 Authorize event: subject, position, prev_digest, device, capabilities ⊆ {Sign, Present, Encrypt}, validity: Option<ValiditySpan> SignedAuthorityEvent LIB-TESTED (construct::authorize_event) Recover is never granted to a device: enforced at N4-C approval (approve_enrollment refuses it), but N1 admission and N4-A evaluation do not enforce it, so a Recover grant authored outside the approval path is still honoured (routed to #2599); the establishment key is never the device (enforced: DeviceIsEstablishmentAuthority)
the edge turns the request into the fact: approve_enrollment (attenuated; refuses another Subject, a wider grant, an empty grant, Recover, the establishment key) SignedAuthorityEvent LIB-TESTED (N4-C) the human decides; the function only refuses what the contract forbids
the event reaches the device and every relying party facts in their stores MISSING — delivery/transport in-process construction is no longer the only path to the fact; carrying it still is fixture-only

Forbidden substitute: a bearer token, invite code, OS account, SSH key, RDP login or TLS client certificate standing in for the Authorize event. These may gate a transport; none of them is device authority.

6.3 What the Home runtime stores

Stored Class Rule
per Subject it serves: the public N1 facts — canonical bodies and witnesses — using the N1-D record layout event_id ‖ signature → canonical body record layout on main (#2800); serving MISSING re-admit every record through admissible_bytes on load; storage never selects branches
device acts (DeviceActV1 bytes + signature) and the VerifiedDeviceAct the runtime obtained for each LIB-TESTED evidence is reproducible from facts + act + E; it is a log, not an oracle
the Home's own per-context device keys (the VM-as-device) — the VM is a device Principal
never: any ContinuityRoot, any other device's secret, any human establishment key, any cross-context mapping of one person's Subjects — R1.2 [HARD]; IS §5.4; I10

6.4 What revocation does

  1. The authority edge authors N1 Revoke { device } at the next position.
  2. Any relying party that has retained that fact evaluates every later act at E ≥ the revoke position and refuses the device (DeviceNotAuthorized). Acts at earlier E remain verifiable: history is not rewritten.
  3. Effectiveness is per relying party, exactly as far as its retained facts go. There is no global instant revocation and no claim of one (#2599 invariant 5; N3 owns propagation).
  4. Replacing a device is Revoke(old) + Authorize(new); the Subject is untouched. Reflashing a device is the same two events; a reflashed device with a fresh secret is a new Principal.
  5. A Home runtime that loses contact with the authority edge cannot revoke anything; it can only stop relaying. Stopping relay is a hosting decision and confers no authority.

6.5 What the TLS trust root is

ICN binds a Principal to a TLS certificate only for nodes, through BindingInfo in the network Hello (bundle.rs:181; IS §9), and that binding is per connection and node-scoped. There is no device-Principal-to-TLS binding for Home transports, and BindingInfo must not be repurposed for humans or devices. Therefore:

  • the thin-client transport (remote desktop to a hosted workstation) keeps its conventional PKI, owned and operated by the deployment track; it is a transport fact, not an identity fact;
  • a Home transport that wants "this connection is device Principal K" uses the N4-D binding: a Present act whose payload is (channel digest, nonce), verified by N4-A over retained facts and then one equality against the channel the relying party sees — LIB-TESTED (N4D_DEVICE_CHANNEL_BINDING.md); what channel digests is the transport profile's to state;
  • nothing about a TLS root ever identifies the human.

6.6 The MAY / MUST NOT list

A Home runtime or deployment MAY rely on: the N4-A verdict and the VerifiedDeviceAct that verify_device_act returns (never bare DeviceAuthorityEvidence from evaluate_device_authority, which checks no signature and names no act); act_id as an act's identity; the refusal classes; the rule that a revoked device is refused at every E at or after the revoke and unaffected before it, under the facts retained (N4-A §2); the N1-D record layout (merged, #2800). No hosted verifier and no consumer that enforces an action on an N4-A result exists today. When one is built, it must enforce on a VerifiedDeviceAct, never on an authority evaluation alone.

It MUST NOT: use a node's transport or network key as a device Principal (a public node key would make the device a correlation handle for the person, IS §2.4); reimplement encodings, the fold, grant semantics or refusal logic in another language; treat any account, key, token, VM or machine as the Subject or derive a SubjectId from one; hold or derive a ContinuityRoot on infrastructure; mint a Subject or a context on a person's behalf; choose E from the act or fall back to the frontier when the act's E is not retained; build an enrollment, revocation or recovery mechanism of its own; promote fixture identities into anything real.


7. Acceptance scenario status

Item Status
1. decision record with classes and owners this document §1
2. fixture-backed test done — PR #2807
3. external verification boundary done to a CLI relying party: N4-B container (LIB-TESTED, vectors) + icnctl device-authority (CLI-REACHABLE). Open: a hosted relying party, deliberately not built
4. GEN stance §4 — new GEN context kinds, routed to #2602; Individual-owned InstitutionalDomain rejected for this use
5. implementable spec §6, now with the request and approval objects real (N4-C §6 restates the contract per phase: first boot, enrollment, runtime, revocation, recovery, contexts, trust)

8. Routing

#2694 (ladder; container as the shared missing boundary) · #2599 (device case; ceremony, device TLS binding) · #2602 (context kinds) · #2806 (ADR-0083 status drift) · PR #2807 (this branch).