PrincipalAttestationConformanceSuite
Reusable contract test suite for hub seams that carry a host-verified identity — any Seam that is also a PrincipalRoster.
When a fabric admits a peer over a connection stamped with a verified identity (via us.tractat.kuilt.core.withPrincipal), the seam must publish that identity on its PrincipalRoster.attestedPrincipals roster, keyed by the peer it was verified against. This suite pins that obligation so a future hub seam that silently drops the identity fails the suite instead of shipping an empty roster (the exact hole a mux-hub seam once shipped: it satisfied : Seam without carrying attestation, and every consumer reads as? PrincipalRoster ?: EMPTY, so a dropped principal is a silent empty map, not a failure).
Subclass and implement newHarness to bind any principal-bearing seam under test. Both reference hub seams — the flat mesh (us.tractat.kuilt.core.fabric.Mesh) and the per-room mux hub (us.tractat.kuilt.core.RoomHubSeam) — subclass it; the next hub seam that forgets the principal is caught here.
Lives in commonMain of :kuilt-conformance (not a module's commonTest) so every principal-bearing seam adapter can subclass it from its own test source set — the same "one suite, every impl passes it" pattern as SeamConformanceSuite and RoomFanoutIsolationConformanceSuite.
The invariants pinned:
Attested carriage — a peer admitted with a principal is reported for that peer (attestedPrincipalIsReportedForAdmittedPeer).
Attested provenance — the roster value is a function of what the host verified alone. What the peer itself says never becomes an attestation, and never outranks the host's (peerAssertedPrincipalNeverReachesTheRoster, hostVerifiedPrincipalOutranksThePeersClaim).
Per-peer keying — distinct peers' principals are keyed independently, and an unattested peer is simply absent, not
null-valued (distinctPrincipalsAreKeyedByPeer, unattestedPeerIsAbsentFromRoster).Reconnect refresh — a peer that reconnects with a new principal supersedes the old (rosterUpdatesPrincipalOnReconnect).
Drop / close cleanup — a dropped peer leaves the roster; a closed seam empties it (droppedPeerLeavesTheRoster, closingTheSeamEmptiesTheRoster).
Carriage is not provenance (#2316)
Carriage — "the principal I handed in comes back out" — is the weaker half, and on its own it is satisfied by a hub that reads the identity out of the joiner's own handshake frame instead of the host-stamped connection. Every carriage property reads back a value the suite itself supplied, so none of them can tell the two sources apart: a fabric that lets a peer assert whatever identity it likes passes all of them. That is the hole this suite was written for ("satisfied : Seam without carrying attestation"), one level up.
What states the difference is AttestationHarness.admitClaiming: it splits the single principal into the one the host verified and the one the peer asserts, and the two provenance properties require the roster to be a function of the first alone. The hook is non-nullable and has no default on purpose. An "my fabric gives a joiner no way to assert an identity" opt-out would move the vacuity one level up, where it is harder to see — and it would also be false: a Seam is a channel over which a peer sends bytes to the hub, so a joiner can always utter a claim. The only question is whether the hub believes it, which is exactly what is under test. There is therefore no "unreachable" arm here and no two-armed fixture; there is one hook every implementor can honour.
A harness that quietly ignored claimed would make both properties vacuous — the recorded "permissive fake makes an enforcement test vacuous" failure. So each property asserts its own precondition before it asserts anything else: it collects the seam's inbound frames and waits to hear the claim come off the wire. A harness that never utters it does not pass; it wedges at TEST_WEDGE_BACKSTOP inside the property that names the claim.
What the provenance properties cannot detect. They drive exactly one channel — the bytes of the first frame the joiner sends, plus the identity it self-asserts in its preamble. A hub that believed a claim arriving some other way — a later frame, a side-band control message, a transport header the harness does not set — still passes. They also say nothing about whether the host's verification was any good: verified is taken on trust, as the fabric's accept handler takes call.principal() on trust.
What is deliberately NOT here: dispossession (#2357)
"A live, host-verified link cannot be dispossessed of its peer identity by a second link the host verified as nothing" is a real obligation, and it is not in this suite, because the two reference seams honestly differ on it and enshrining an arm for the one that fails would make the defect conformant.
The difference is how many live links one peer id can have. A mux hub holds two at once — the claimant registers alongside the peer, both connections stay open — so it can, and now must, refuse the claimant (RoomHubSeamUnattestedClaimTest). A mesh cannot: duplicate links to one id are collapsed by a canonical-nonce tiebreak and the loser is closed, so exactly one link survives and the roster, being derived from the live link set, describes it accurately either way. When a claimant wins there, the peer really is gone; reporting it unattested is correct, not an erasure. What the mesh cannot do is refuse the claimant on attestation grounds — the tiebreak is a pure function of the two nonces precisely so both ends derive the same survivor with no coordination, and a local veto would have each end keep a different link and close the one its peer kept. Its defence is deployment policy instead, and unlike us.tractat.kuilt.core.RoomAuthorizer it can express one: LinkAdmission receives the principal (MeshAdmissionTest.bindingMismatchIsRejectedBeforeDedupLottery).
So the obligation lives with the seam that can meet it. A future hub seam that also holds concurrent links for one id should be held to RoomHubSeamUnattestedClaimTest's shape; the day there are two such seams, that is the moment to lift it in here.
Mutation receipt (#2316)
Baseline and the reverted control arm are 16/16 green (8 properties × 2 subclasses).
| Mutation | Reds | Stays green |
|---|---|---|
M1 MuxServerLoom.readLoop: principal ?: payloadAsPrincipal(body) — fall back to the identity the client announced in its first frame | peerAssertedPrincipalNeverReachesTheRoster (RoomHub) — 1/16 | all 6 carriage properties, both subclasses |
M2 MuxServerLoom.readLoop: payloadAsPrincipal(body) ?: principal — prefer the client's announcement | both provenance properties (RoomHub) — 2/16 | all 6 carriage properties, both subclasses |
M3 MeshSeam.readLoop: re-stamp the link's principal from each inbound frame body | both provenance properties (Mesh) — 2/16 | all 6 carriage properties, both subclasses |
M4 MeshSeam.handshakeLink: principal ?: Principal(remote.peerId.value) — trust the self-asserted id | peerAssertedPrincipalNeverReachesTheRoster and unattestedPeerIsAbsentFromRoster, on both subclasses (a room rides a per-connection hubMesh) — 4/16 | hostVerifiedPrincipalOutranksThePeersClaim |
The greens are the point of the table. M1–M3 are invisible to every pre-existing property here: they can only fire on a frame body, and no carriage property sends one carrying an identity — so those three are what the new properties buy. M4 is the converse, listed to mark where they are not load-bearing: unattestedPeerIsAbsentFromRoster already caught it. M4's own green is honest too — its ?: cannot fire when the host verified something, which is exactly the half hostVerifiedPrincipalOutranksThePeersClaim exists for and M2/M3 do red.
Read the shape, not just the count: under M1 the two refusal assertions red while the rig assertion ("a host-verified admission is reported in this run") stays green — the roster is alive and the failure is a refusal that did not happen, not a roster that never filled. Under M2/M3 hostVerifiedPrincipalOutranksThePeersClaim reds with expected verified-guest but was verified-admin — the forged value in the roster by name.
Virtual time convention: every test runs under StandardTestDispatcher and awaits admission/removal on observable roster state (attestedPrincipals.first { … }) rather than polling after advanceUntilIdle, so the admit/drop path is driven deterministically. The runTest ceiling is TEST_WEDGE_BACKSTOP — a generous wedge backstop, never a tight assertion (#1739).
Types
A live principal-bearing seam under test plus the operations to admit, reconnect, drop, and close peers whose connection carries a host-verified Principal.
Functions
A peer admitted with a verified principal is reported on the roster for that peer.
Closing the seam empties the roster with the membership.
Two peers admitted with distinct principals are keyed independently by peer id.
A dropped peer's roster entry is removed; a bystander's entry survives.
Security. When the host's verification and the peer's claim disagree, the host wins. This is the half peerAssertedPrincipalNeverReachesTheRoster cannot reach: a hub that consults the peer's claim only when it has no stamp of its own passes that one and fails this one, and a hub that lets the claim override passes neither.
Provide a fresh harness for one test.
Security. An identity a peer asserts about itself never becomes an attestation. The impostor loads both channels a joiner controls with the same claim — the id it self-asserts in its preamble, and the body of the frame it sends — over a link the host verified as nothing at all. It must stay absent from the roster, and its claim must not be attributed to anyone.
A peer that reconnects with a new principal supersedes its prior roster entry.
An admitted-but-unattested peer (a null principal) is absent from the roster — never a null entry. Verified non-vacuously: an attested peer admitted the same way is present.