Attesting

The protocol specification for creating verifiable attestations.

Attesting is the Write Path of the Fide Context Protocol. This page defines the protocol specification for how drafted statements are signed into verifiable Attestations.

SDK Implementation

For working code examples and implementation guidance, see Attesting & Batching Guide. This protocol page documents the underlying specification so implementations across any language can stay interoperable.

The Attestation Pattern

A Signed Statement has one or more verifiable Attestations.

The PROV-O pattern: Statementprov:wasGeneratedByAttestation.

Drafting is Local

Statements are drafted and signed privately on your client or agent. No data leaves your secure context until you explicitly broadcast it.


Attestation Structure

An Attestation entity contains all the data needed for verification:

FieldKeyDescription
MethodmSigning standard identifier (CAIP-122), e.g., eip712, ed25519
UseruCAIP-10 signer address with chain context, e.g., eip155:1:0x...
RootrMerkle tree root commitment (hex string)
SignaturesCryptographic signature over the Merkle root

The Attestation Fide ID (did:fide:0xa0...) is derived by hashing the canonical JSON of these fields using calculateFideId.


JSONL Storage Format

When attestations are written to JSONL (for broadcasting), the format stores the minimum needed for verification and materialization.

Minimum Per Statement

Each statement in the JSONL d array uses short keys: s, sr, p, pr, o, or.

KeyMeaningWhy Stored
sSubject Fide IDVerification + lookup
srSubject raw identifierIndexer materialization; cannot be derived from hash
pPredicate Fide IDVerification + lookup
prPredicate raw identifierIndexer materialization
oObject Fide IDVerification + lookup
orObject raw identifierIndexer materialization

Entity type and source type are not stored separately. They are encoded in the Fide ID (the first two hex characters after 0x) and can be derived by indexers using parseFideId or equivalent. This keeps the JSONL lean while preserving full protocol semantics.


Batch Attestations

FCP uses Merkle trees to enable efficient batch signing:

Gather Statement Fide IDs

Collect the Statement Fide IDs (did:fide:0x00...) you want to sign together. Any statements can be batched—different subjects, predicates, and objects are all valid.

Build Merkle Tree

Organize the Statement Fide IDs into a binary Merkle tree. Hash pairs of nodes iteratively until a single Merkle Root remains.

The Merkle root is a single commitment to the entire batch.

Sign the Root

Sign the Merkle root using one of the supported signing methods. This single signature covers all statements in the batch.

Create Attestation Entity

Combine the signature, method, CAIP-10 user, and Merkle root into the attestation data structure. Derive the Attestation Fide ID from the canonical JSON.

For each statement in the batch, create a provenance statement:

Statement → prov:wasGeneratedBy → Attestation

This links the content statement to the attestation that proves its authorship.

SDK Implementation

The SDK's createAttestation() function handles Merkle tree construction and signing. Provenance links are created separately via createProvenanceStatements(). See Attesting & Batching Guide for the complete implementation.


Selective Disclosure

The power of Merkle trees: you only need to disclose the specific statement you want to prove—not the entire batch.

How it works:

  1. The verifier receives: the statement Fide ID, the Merkle proof (2-3 hashes), and the attestation data
  2. The verifier reconstructs the path from the statement to the Merkle root
  3. If the reconstructed root matches the signed root, the statement is verified

Use cases:

  • Employee proves their credential without revealing other employees in the batch
  • Medical record: prove one diagnosis without revealing full history
  • Large batches: verify one statement without downloading the entire batch

SDK Implementation

See Attesting & Batching Guide for working code examples.


Verification Requirements

To verify a signed statement, implementations must check:

  1. Attestation ID Derivation — Re-derive the Attestation Fide ID from the attestation data and confirm it matches
  2. Signature Validity — Verify the signature over the Merkle root using the method and address specified
  3. Merkle Proof — Verify the statement is included in the batch (proof path leads to signed root)
  4. Authority (application-layer) — Confirm the signer has the right to make this statement (e.g., via sec:controller)

SDK Implementation

The SDK provides verifyAttestation() which handles steps 1-3 automatically. See Attesting & Batching Guide for details.


Signing Methods

The signing method identifies which cryptographic protocol was used. FCP uses CAIP (Chain Agnostic Improvement Proposals) standards so any blockchain or signature system can verify statements.

MethodBlockchainStandard
eip712Ethereum (EVM)EIP-712 Typed Data (recommended for Ethereum)
eip191Ethereum (EVM)EIP-191 Personal Sign
ed25519UniversalEd25519 (native, zero-dependency)
bip322BitcoinBIP-322 Modern
solana:signMessageSolanaSolana Message Signing
cosmos:ADR-036CosmosCosmos ADR-036

The method tells the verifier exactly which signing procedure to use—not just the algorithm name.

SDK Implementation

The SDK provides implementations for Ed25519, EIP-712, and EIP-191. See Attesting & Batching Guide for usage examples.

CAIP-10 User Addresses

The user address uses CAIP-10 format: chainNamespace:chainReference:address.

For chainless signing methods (for example ed25519), an empty chainReference is valid and expected, e.g. ed25519::abc123....

Why this matters:

  1. Verification Routing — Different chains use different cryptographic standards

    • eip155:1:0x... → secp256k1 + Keccak verification
    • solana:mainnet:5ey... → Ed25519 + SHA-256 verification
  2. Replay Attack Prevention — Testnet and Mainnet signatures are distinguishable

    • A signature for eip155:11155111:0x... (Sepolia) cannot be replayed as eip155:1:0x... (Mainnet)
  3. EIP-712 Domain Enforcement — The chain ID is part of the cryptographic math; signatures are mathematically invalid on different chains

BlockchainCAIP-10 FormatExample
Ethereum Mainneteip155:1:0x...eip155:1:0x742d35Cc...
Ethereum Sepoliaeip155:11155111:0x...eip155:11155111:0x742d35...
Ed25519 (chainless)ed25519::...ed25519::abc123...
Bitcoinbip122:000000000019d6...:1A1zP1eP5...Bitcoin address with mainnet chain ref
Solana Mainnetsolana:5eykt4UsFv2P6tnvPXJ5:...Solana program ID + address

EIP-712 Domain Configuration

For Ethereum, statements are signed within the Fide Context Protocol domain:

Domain:
  name: "Fide Context Protocol"
  version: "1"
  chainId: <chain-specific, e.g., 1 for Mainnet>
  verifyingContract: "0x0000000000000000000000000000000000000000"

The chainId becomes part of the cryptographic math itself. A signature created on Mainnet (chainId: 1) is mathematically incompatible with Testnet (chainId: 11155111).

Secure Key Management

FCP does not enforce how keys are stored or managed—that is the responsibility of the application or agent.

However, proper key management (creation, rotation, lifecycle) is critical for trust. See the SDK Key Utilities for tools to generate and manage compliant keys.

On this page