Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Setup and shape

audience: ai

Three things are fixed before the signer can run: the crate-level dependencies, the signer-δ coalition shape, and the Config that pins scheme, threshold, committee size, and minimum attestation floor. The backing crate is at examples/signer/; every type named below is exported from it.

Dependencies

Cargo.toml, [dependencies]
mosaik       = "0.3"
coalition    = "0.1"
signer       = "0.1"   # this book's crate
tokio        = { version = "1", features = ["full"] }

The SignableMessage trait

The signer is generic over message type. Downstream crates implement SignableMessage for their concrete shape; the trait’s TYPE_TAG folds into the organism’s identity preimage so two signer instances that differ only in message type derive distinct ids.

pub trait SignableMessage: 'static {
    /// Domain-separation tag folded into every
    /// request's digest.
    const TYPE_TAG: &'static [u8];

    /// Bound on the request body size; committee
    /// bandwidth stays predictable.
    const MAX_BODY_BYTES: usize;
}

// For the generic OpaqueMessage case the example ships:
pub struct OpaqueMessage;
impl SignableMessage for OpaqueMessage {
    const TYPE_TAG: &'static [u8] = b"opaque-bytes.v1";
    const MAX_BODY_BYTES: usize   = 64 * 1024;
}

A real deployment signing beacon-chain attestations would carry TYPE_TAG = b"eth2.attestation.phase0"; one signing coalition-level retirement commits would carry b"coalition.retirement.v1".

The signer organism’s Config

The organism’s identity preimage folds every parameter that changes behaviour:

use coalition::UniqueId;
use signer::{AttestationLevel, Config, OpaqueMessage, SignatureScheme};

pub const SIGNER_CFG: Config<'static, OpaqueMessage> =
    Config::new(
        /* instance_name         */ "signer-δ.default",
        /* scheme                */ SignatureScheme::BlsAggregateG1,
        /* threshold             */ 5,
        /* committee_size        */ 7,
        /* min_attestation_level */ AttestationLevel::TdxRemote,
        /* committee_pk_digest   */ COMMITTEE_PK_V1,
    );

const COMMITTEE_PK_V1: UniqueId =
    mosaik::unique_id!("signer-δ.committee.aggregate-pk.v1");

Identity derivation (as implemented in Config::organism_id):

  SIGNER(cfg) = blake3(
      "signer|"
      || SCHEMA_VERSION_U8
      || "|" || instance_name
      || "|" || M::TYPE_TAG
      || "|" || scheme_discriminant
      || "|" || threshold || committee_size
      || "|" || min_attestation_level_discriminant
      || "|" || committee_pk_digest
  )

Any change to any of those parameters is a new organism_id. That includes:

  • A new signature scheme — every consumer TicketValidator that pinned the old id stops admitting outcomes from the new one until it republishes.
  • A raised attestation floor — consumers that pinned the old floor continue admitting the organism at the old level; a rotation to a new id forces an explicit consumer-side consent.
  • A new committee DKG key — the standard key- refresh event (see sustainability).

The signer-δ coalition

Packaging wrapper, one member:

use coalition::{
    CoalitionConfig, OrganismRef,
    COALITION_ROOT_SEED, SCHEMA_VERSION_U8,
    RetirementPolicy,
};

pub const SIGNER_ORG: OrganismRef<'static> =
    OrganismRef::by_stable_id(
        "signer",
        const_blake3!(
            b"instance|signer-δ.signer|",
            UNIVERSE.bytes(),
        ),
    );

pub const SIGNER_DELTA: CoalitionConfig<'static> =
    CoalitionConfig {
        schema_version:    SCHEMA_VERSION_U8,
        coalition_seed:    COALITION_ROOT_SEED,
        instance_name:     "signer-δ",
        lattices:          &[],
        organisms:         &[SIGNER_ORG],
        atlas:             None,
        almanac:           None,
        chronicle:         None,         // added in ch. 7
        compute:           None,
        randomness:        None,
        ticket_issuer:     None,
        retirement_policy: RetirementPolicy::CONSERVATIVE,
    };

Consumers need not reference signer-δ; they reference the signer organism directly via their own OrganismRef. The coalition wrapper exists for the operator’s retirement chain and optional Chronicle.

Attestation-level ladder at a glance

  Software                    // declared-identity only
    ▲
  TdxLocal                    // TDX quote, peer-verified
    ▲
  TdxRemote                   // TDX quote, DCAP-verified
    ▲
  TdxRaTlsBidirectional       // bidirectional RA-TLS
    ▲
  TdxPlusReproducibleBuild    // RA-TLS + reproducible-build root

Config.min_attestation_level is the floor the organism accepts. Providers above the floor compete for committee slots; consumers above the floor compose their own higher-floor TicketValidator clause independently (binding).

What is published

Three values the signer operator publishes to consumers:

  1. SIGNER_CFG.organism_id() — the signer’s stable id.
  2. SIGNER_CFG.committee_pk_digest — the committee’s DKG aggregate public key digest.
  3. SIGNER_CFG.min_attestation_level — the declared floor.

Any three-way combination of those values is sufficient for a consumer to compose admission without out-of-band handshake.

Forward

Chapter 2 (binding) walks the consumer side: referencing the signer, composing a TicketValidator that gates on aggregate attestation, and verifying committed outcomes.