Skip to content

v39.0.0: the node can no longer sign a third party's claim into the mesh — age assurance, watchlist enable and track record now wait for an actor that never comes #533

Description

@emooreatx

Adopting persist v39.0.0 / edge v19.0.0 leaves three safety tests red, and they are not fixture noise — they are the API contract.

What changed

attestation_promote re-signed every promoted row with THIS NODE's key. v39.0.0 replaced it with enter_mesh + widen_audience, and Engine::custody_for now decides custody from the row:

row signer in hand custody
unsigned, attested by this node this node signs as the actor
unsigned, attested by another key that key's signer the actor signs now
unsigned, attested by another key none AwaitingActor — it waits

That third row is the one we land in, and it is v39 working correctly: the fabric may no longer author a claim it is only carrying.

Where it bites

Three paths write a row attested by someone who is not this node, from an unsigned request, and used to rely on the node signing it into the federation:

  • safety::age::emit_age_assuranceattesting_key_id: subject_key_id. Age assurance is a SELF-declaration; the subject attests it.
  • safety::watchlist::{enable_watchlist, disable_watchlist}attesting_key_id: signer_key_id, the enabling authority.
  • safety::moderation (record) and the seed_track_record shape behind existence_invariant_* — a member's own track record.

Each now returns Ok(AwaitingActor) and the row stays local-tier. The read side is federation-tier, so the feature reads as absent. Nothing errors. I added tracing::info!/warn! at each site so it is at least legible, and a warn! specifically on the watchlist WITHDRAW, where a silent wait is the worst of the three (peers keep reading the enable).

Failing, with the assertion each makes:

age_assurance_emit_read_roundtrip_and_misdeclaration_does_not_slash
existence_invariant_auto_promotes_highest_track_record_on_lapse
    "with a live moderator the merit rows DO reach federation tier"  0 vs 3
watchlist_publish_hook_is_opt_in_and_defers_the_matcher              0 vs 1

The decision this needs

The claim genuinely belongs to its author, so one of:

  1. The client signs. These endpoints accept a claim plus its author's signature, stored as the row's base scrub at write; the node then co-scrubs at the crossing (custody_for's first arm, no actor signer needed). Truest to CC, and the shape edge assumes — Signers.actor is documented as "the human's FedID signer as they hit send".
  2. The owner-signer capsule. src/owner_signer_capsule.rs already exists for exactly this asymmetry — its module doc says the gap it closes is that "an agent recording a human authorization ... could not put the authorizing human's own signature on it". It holds an Arc<LocalSigner> and offers one verb. Threading it into these paths would cover the case where the author IS this node's owner, which is the single-user node. It cannot cover a third party, and it would need an accessor enter_mesh can take (&LocalSigner), which widens a surface that was deliberately narrow.
  3. Re-model the claim as node-attested about a subject — the node attests "subject X declared adult" rather than the subject attesting it. This changes authorship semantics and I do not think it is right for a self-declaration, but it is the only option that needs no signature from anyone else.

(1) for the general case and (2) as the bridge for owner-authored rows looks right to me, but this is an authorship decision, not an adoption detail, so I have not made it under a substrate repin.

Status

Everything else on the triple is green: 113/115 test binaries, 1194 tests, all 15 release gates, clippy and fmt clean. contacts_chat is 25/25 — the chat plane took its own v39 work (the reader now folds placement widenings, and a message's community identity is the widened row's id).

The other red suite, equivocation, is blocked upstream on CIRISPersist#801 (a widening drops the claim's asserted_at, so a signed instant is unrecoverable off-node) and is tracked separately.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions