Skip to content

sym 0.13.14 — a peer's admission verdict is observable as it lands

Choose a tag to compare

@sym-bot sym-bot released this 01 Oct 16:35
· 187 commits to main since this release
0e5e52c

Added — a peer's admission verdict is observable as it lands (attestation-received)

A receiver signs an Admission Attestation for every CMB it gates and gossips it to the room. Until now the
author, or anything watching the mesh, could see those verdicts only by polling attestationsFor(cmbKey).
The node now emits attestation-received the first time it verifies and records a peer's attestation.

Fields covered by the attester's signature, passed on as signed:

  • of (the gated CMB key), by (the attester's nodeId), at, roster, seq and prev (its place on the
    attester's hash chain);
  • verdict, and categories: the seven CAT7 fields (focus / issue / intent / motivation / commitment /
    perspective / mood), each carrying one of admit / guard / redundant / reject / silent, as the strings the
    signature covers. Any other key in the frame is unsigned and dropped;
  • role, the role the attester claims. A node can stamp any role and still sign validly;
  • sig and sigAlg, so a consumer holding the attester's key can re-check the event with
    verifyAttestation(event, key) instead of trusting verified.

Fields this node adds, which the signature does not vouch for:

  • verified: true, and keySource, where the verifying key came from: anchor, grant, or handshake. A
    handshake key is trusted on first use: it belongs to whoever completed the handshake under that nodeId;
  • roleResolved and roleMatches, the role this node's grant chain resolves for the attester, and
    roleClaimed, the claim roleMatches compares against (participant when the attester stamped none).
    Both are compared as strings, the form the signature covers, so a claim that signs identically matches
    identically.
    Weight a verdict by these, not by role;
  • methodUnsigned, the evaluation method (heuristic / neural). It is outside the signed bytes, so a relay
    could have changed it;
  • byName, the name the attester announced when it connected, only when it is a direct peer. It is a label,
    not an authenticated name: by is the identity;
  • from / fromPeerId, the peer that delivered the frame, and relayed: true when that peer is not the
    attester, false when it is, null when the deliverer is unknown;
  • receivedAt, this node's clock, not the attester's at.

A duplicate, a rate-limited copy, a roster mismatch, an unknown attester or a bad signature emits nothing.
Neither does this node's own verdict: read that from svaf-decision or attestationsFor().

The event holds only primitives and is frozen. A field the attester filled with an object is given as the
string its signature covers, so a listener cannot change the stored record or what the next listener sees.
Each listener is called on its own, in registration order, from a snapshot taken when dispatch starts. The
calls are synchronous, on the frame-ingest path: gossip to the room has already gone out, but a listener that
blocks delays this node's next frames, so hand heavy work to a queue. A
listener that throws (any value), or an async listener whose promise rejects, is logged with the attestation
it was handling. It does not stop later listeners, undo the ingest, or become an unhandled rejection. An event
that cannot be built is reported on metric as attestation-event-dropped. That one metric is delivered to
each metric listener the same isolated way, not through emit(), so code that wraps emit does not see it.
The attestation is still recorded. With no listener, none of this work is done.