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
TicketValidatorthat 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:
SIGNER_CFG.organism_id()— the signer’s stable id.SIGNER_CFG.committee_pk_digest— the committee’s DKG aggregate public key digest.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.