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: Statement → prov:wasGeneratedBy → Attestation.
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:
| Field | Key | Description |
|---|---|---|
| Method | m | Signing standard identifier (CAIP-122), e.g., eip712, ed25519 |
| User | u | CAIP-10 signer address with chain context, e.g., eip155:1:0x... |
| Root | r | Merkle tree root commitment (hex string) |
| Signature | s | Cryptographic 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.
| Key | Meaning | Why Stored |
|---|---|---|
s | Subject Fide ID | Verification + lookup |
sr | Subject raw identifier | Indexer materialization; cannot be derived from hash |
p | Predicate Fide ID | Verification + lookup |
pr | Predicate raw identifier | Indexer materialization |
o | Object Fide ID | Verification + lookup |
or | Object raw identifier | Indexer 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.
Link via Provenance
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:
- The verifier receives: the statement Fide ID, the Merkle proof (2-3 hashes), and the attestation data
- The verifier reconstructs the path from the statement to the Merkle root
- 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:
- Attestation ID Derivation — Re-derive the Attestation Fide ID from the attestation data and confirm it matches
- Signature Validity — Verify the signature over the Merkle root using the method and address specified
- Merkle Proof — Verify the statement is included in the batch (proof path leads to signed root)
- 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.
| Method | Blockchain | Standard |
|---|---|---|
eip712 | Ethereum (EVM) | EIP-712 Typed Data (recommended for Ethereum) |
eip191 | Ethereum (EVM) | EIP-191 Personal Sign |
ed25519 | Universal | Ed25519 (native, zero-dependency) |
bip322 | Bitcoin | BIP-322 Modern |
solana:signMessage | Solana | Solana Message Signing |
cosmos:ADR-036 | Cosmos | Cosmos 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:
-
Verification Routing — Different chains use different cryptographic standards
eip155:1:0x...→ secp256k1 + Keccak verificationsolana:mainnet:5ey...→ Ed25519 + SHA-256 verification
-
Replay Attack Prevention — Testnet and Mainnet signatures are distinguishable
- A signature for
eip155:11155111:0x...(Sepolia) cannot be replayed aseip155:1:0x...(Mainnet)
- A signature for
-
EIP-712 Domain Enforcement — The chain ID is part of the cryptographic math; signatures are mathematically invalid on different chains
| Blockchain | CAIP-10 Format | Example |
|---|---|---|
| Ethereum Mainnet | eip155:1:0x... | eip155:1:0x742d35Cc... |
| Ethereum Sepolia | eip155:11155111:0x... | eip155:11155111:0x742d35... |
| Ed25519 (chainless) | ed25519::... | ed25519::abc123... |
| Bitcoin | bip122:000000000019d6...:1A1zP1eP5... | Bitcoin address with mainnet chain ref |
| Solana Mainnet | solana: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.