TEEProverRegistry.
It discovers Base TEE prover instances, obtains an AWS Nitro attestation for each enclave signer,
generates P-384 verification hints locally, caches the attestation certificate chain on L1, and
submits the final signer registration.
This flow replaced the previous RISC Zero attestation proof and external Boundless proving path in
Cobalt; see Hinted Registration Migration.
There is no runtime backend selector or fallback to ZK verification. Registration requires the
hinted-compatible registrar and prover-host signer API.
Responsibilities
The registrar discovers TEE prover instances, obtains and validates signer attestations, generates P-384 hints, caches certificates, and registers signers. It also deregisters orphaned signers and can monitor AWS certificate revocation lists. The registrar does not select the accepted enclave image. Registration records the attested PCR0, butTEEVerifier
accepts a signer only when its recorded image hash matches the active TEE_IMAGE_HASH. This allows
the next image’s signers to be registered before an image rotation without allowing them to produce
accepted proofs early.
Architecture
The onchain validation stack contains three contracts:TEEProverRegistry holds an immutable NitroValidator reference. The registrar discovers
CertManager through TEEProverRegistry.NITRO_VALIDATOR() and NitroValidator.certManager();
operators do not configure those addresses separately.
Driver Loop
The registrar runs one polling loop:- Query AWS ALB and EC2 for the current prover instances.
- Probe
/readyzon every non-draining target. The load balancer’s/healthzis registration-gated, so it cannot be used to bootstrap registration. - Resolve signer public keys and attestations concurrently, bounded by
max_concurrency. - Start one registration task per eligible signer and cancel tasks whose signer is no longer eligible.
- Read the onchain signer set and deregister unprotected signers when discovery was conclusive.
- Sleep for
poll_interval, or stop on cancellation.
/readyz are eligible for new registrations. Draining instances still
contribute their known signer addresses to the active set so a rotation does not immediately
deregister them.
When an instance disappears or becomes unhealthy, the registrar preserves its last-known signers
for instance_cache_ttl_cycles. It skips the entire orphan pass while any instance remains
unresolved. Pending registration tasks are also protected from orphan cleanup.
Discovery supports AWS ELBv2 target groups whose targets are EC2 instances. Targets whose IDs do
not start with i- are ignored. AWS API errors and missing EC2 data abort the tick and skip orphan
cleanup, but an empty target group is a conclusive result. The last-known-signer cache is in memory
and starts empty after a process restart. Operators must therefore configure and monitor the target
group carefully: a cold registrar pointed at an empty target group has no cached signers to protect.
Attestation Challenge
Each signer receives a deterministic 32-byte nonce:Attestation Nonce
- the attested
public_keyderives to the expected signer address; - the attested nonce exactly matches the deterministic challenge;
- PCR0 is present, is 48 bytes, and is not the all-zero debug-mode measurement;
- the certificate chain starts at the pinned AWS Nitro root;
- the certificate chain is ordered parent-first and ends in one leaf certificate; and
- the attestation remains within the configured local freshness window.
TEEProverRegistry independently enforces timestamp freshness,
but nonce matching is a registrar policy check because NitroValidator only exposes the signed
nonce to its caller.
Registration Plan
The registrar preserves the exact protected-header and payload encodings from the inputCOSE_Sign1 document when constructing the signed Sig_structure. Re-encoding signed CBOR would
change the message and invalidate the AWS signature.
For parity with the pinned NitroValidator, the registrar accepts an optional compact 0xD2 tag
followed by the compact 0x84 COSE_Sign1 array, the ES384 protected header 0x44a1013822, a
96-byte P-384 signature, and no trailing COSE or TBS data. The payload, pcrs, and cabundle
containers may use definite or indefinite lengths. Unknown payload keys are skipped, but recognized
keys must be unique.
The parsed plan contains:
- the signer derived from the 65-byte
0x04 || x || ysecp256k1 public key; - PCR0, timestamp, and nonce;
- the pinned root certificate;
- non-root CA certificates followed by the leaf certificate;
- the exact attestation to-be-signed bytes and 96-byte P-384 signature; and
- cache and revocation identifiers matching
CertManager.
keccak256(root DER). Every non-root cache key is
keccak256(TBSCertificate DER), excluding the malleable outer ECDSA signature. Non-root revocation
uses keccak256(issuerHash || serialHash). issuerHash hashes the issuer Name content octets,
excluding its DER tag and length. serialHash hashes the serial INTEGER content octets, including a
leading 0x00 used for DER sign extension. These identities remain stable across equivalent outer
certificate encodings.
P-384 Hints
AWS signs Nitro certificates and attestations with ECDSA over P-384. P-384 verification requires many modular inversions, which are expensive to compute in the EVM. The registrar computes each inverse offchain and supplies it as a hint. For every division byb modulo a prime m, the contract checks:
Inverse Check
Certificate Cache
CertManager stores the pinned AWS Nitro root at deployment. The registrar processes every
remaining certificate in parent-first order:
- Read the candidate’s cached metadata and revocation state.
- If cached, require the expected CA or leaf role, an unexpired validity period, the original parent binding, and a complete unrevoked path to the pinned root.
- If not cached, call
verifyCACertWithHints()orverifyClientCertWithHints()with the DER certificate, parent cache key, and signature hints. - Re-read state after the transaction. A transaction error is treated as success if the expected usable cache entry is now present.
Each signature verification is split into its own transaction so it remains below the EIP-7825
per-transaction gas limit. Final registration intentionally supplies no certificate hints;
NitroValidator succeeds only if the complete chain is already cached and usable.
Final Registration
After the cache is ready, the registrar calls:Registration Call
NitroValidator, then applies Base-specific policy:
- Reject attestations at least 60 minutes old.
- Reject attestations whose second-level timestamp is greater than or equal to
block.timestamp. - Require PCR0 at index zero, exactly 48 bytes, and not the debug-mode measurement.
- Require a 65-byte uncompressed secp256k1 public key.
- Derive the signer as the last 20 bytes of
keccak256(x || y). - Store the signer as registered and record
keccak256(PCR0)as its image hash.
isRegisteredSigner(signer). An observed registration is treated as success. Retryable transaction
errors use bounded exponential backoff; reverted receipts and non-retryable errors fail the current
task.
Certificate Revocation
CertManager maintains a durable revocation set and an immutable pinned AWS Nitro root. The owner
can revoke the root to halt new registrations, unrevoke the root, set the non-root revoker, and
unrevoke certificate identities. The revoker role can revoke non-root issuer/serial identities.
Revocation is checked during cold verification, cache reuse, and the final cached-chain walk.
Certificate revocation and expiration do not invalidate previously registered signers. Deregister
affected signers separately.
For every registration attempt, the registrar checks the pinned root and every planned certificate
against CertManager.isRevoked(). A confirmed onchain revocation rejects the registration.
When CRL fetching is additionally enabled, the registrar:
- Fetches CRLs only from allowlisted AWS Nitro hosts, without redirects and with a 10 MiB response limit.
- Matches intermediate certificate serial numbers against the CRLs.
- Calls
CertManager.revokeCert()for confirmed revocations before rejecting the registration.
Orphan Deregistration
After registration task reconciliation, the registrar computes:Orphan Formula
deregisterSigner() for each orphan only when discovery completed without unresolved
instances and the configured last-known-signer grace period has expired. Deregistration removes the
registered flag and stored image hash. This flow assumes one registrar controls a given registry;
independent registrars would otherwise classify each other’s signers as orphans.
Hinted Registration Migration
Cobalt replaced ZK-proved signer registration with the hinted flow described on this page. Before Cobalt, the registrar sent each Nitro attestation to an external proving service (Boundless or a self-hosted RISC Zero prover). That service produced a RISC Zero proof that the attestation and its AWS certificate chain were valid, and the registrar submitted the proof toTEEProverRegistry. This added an external availability dependency and could delay new signer
registration while the proof was generated. The hinted flow verifies the attestation directly on
L1, so registration is faster and no longer depends on an offchain proving service.
The migration upgraded the existing TEEProverRegistry proxy implementation. It preserved the
proxy storage layout, owner, manager, game type, proposers, registered signers, and stored signer
image hashes. The active registrar and registry support only the new three-argument hinted API.
The migration did not change:
- Nitro enclave signer generation or attestation production;
- PCR0-based image selection in
TEEVerifieror theTEE_IMAGE_HASHit checks; AggregateVerifierbehavior;- TEE proposal and dispute proof formats; or
- SP1 state-proof behavior.
Operator Inputs
A registrar requires:- an L1 RPC endpoint and
TEEProverRegistryaddress; - AWS region and ALB target group ARN;
- the prover JSON-RPC port;
- an L1 transaction signer and transaction-manager limits;
- attestation freshness, polling, timeout, concurrency, cache-grace, and retry settings; and
- health, logging, and Prometheus metrics settings.
CertManager revoker role. In the current CLI,
--crl-nitro-verifier-address or BASE_REGISTRAR_CRL_NITRO_VERIFIER_ADDRESS enables CRL fetching.
This legacy-named value is only an enable flag; contract discovery still follows the registry to
NitroValidator and then CertManager.
Safety Requirements
A conforming implementation must preserve these properties:- Preserve the exact signed COSE encodings when constructing the attestation TBS.
- Match the attested signer and deterministic nonce before any transaction is submitted.
- Pin the AWS Nitro root and reject malformed, expired, revoked, or parent-mismatched chains.
- Generate hints in the exact verifier order and rely on onchain checks for every supplied inverse.
- Cache certificates parent-first and recover by reading onchain state after every ambiguous result.
- Recheck freshness before each costly transaction in a cold registration.
- Recheck registration state before submission and after ambiguous transaction errors.
- Do not deregister signers while discovery is unresolved or during the configured grace period.
- Keep PCR0 acceptance at proof-submission time so image rotations can be staged safely.