Here are five concrete reasons why logs alone are insufficient for agent trust:
1. Logs Are Controlled by the Party Being Evaluated
An agent operator can edit, rotate, or simply not retain logs. When a buyer asks "did your agent stay within scope during the last 90 days?" and you hand them a CSV export from your own logging system, there is no chain of custody. The buyer has no way to distinguish between an honest operator and a motivated one.
A cryptographically signed attestation from an independent evaluator β with the issuer DID resolvable on-chain β breaks that dependency. The buyer verifies the signature, not your word.
2. Scores Without Provenance Are Marketing
A number on a trust leaderboard means nothing unless the buyer can trace: who ran the evaluation, what task set was used, what methodology applied, what period it covers, and whether any incidents were excluded. A VC with a credentialSubject that embeds evaluationMethodology, taskSetId, seeds, and taskCount gives the buyer all of that in a single artifact they can audit.
Without provenance, trust scores are indistinguishable from marketing copy. With provenance embedded in a signed credential, they are actuarial data.
3. Agent Behavior Changes Over Time β Point-in-Time Proof Is Critical
Models are fine-tuned. Prompts are updated. Guardrails are adjusted. An agent that scored 847 in January may behave differently in July after a model update. Attestations with validFrom / validUntil timestamps and model provenance (weights hash) let buyers understand which version of an agent earned a given record.
The W3C VC 2.0 spec (January 2024) provides validFrom and validUntil at the top level of the credential for exactly this reason. An attestation that doesn't specify its temporal scope is useless for anything requiring time-bounded compliance.
If an agent earns behavioral attestations on Platform A but wants to deploy on Platform B, there must be a mechanism for Platform B to verify those claims without calling Platform A's API β which may be unavailable, untrustworthy, or simply unwilling to cooperate.
W3C DID resolution solves this. Platform B resolves the issuer DID (did:web:armalo.ai) to a DID Document, extracts the public key, and verifies the signature locally. No API call to Platform A required. The trust is cryptographic, not organizational.
5. Compliance and Insurance Require Provable Claims, Not Best Efforts
EU AI Act Article 9 requires "risk management systems" for high-risk AI. NIST AI RMF Govern 1.1 requires "policies, processes, procedures, and practices" for AI risk. Insurance underwriters need actuarial data before they can price AI liability coverage.
None of these require perfection. All of them require evidence. A signed, timestamped attestation saying "this agent operated for 10,000 hours without a severe behavioral incident, as audited by methodology X on task set Y" is evidence. A dashboard screenshot is not.
The Attestation Taxonomy: 7 Types With Schema Examples
Not every behavioral claim deserves the same schema. Here are the seven primary attestation types for agent behavioral history, with canonical credentialSubject shapes for each.
Type 1: Behavioral Baseline Attestation
Answers: "What could this agent do, measured how, when?"
This is the most fundamental attestation. It records a point-in-time evaluation result with full methodology provenance.
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://armalo.ai/schemas/v1/behavioral-baseline"
],
"type": ["VerifiableCredential", "BehavioralBaselineAttestation"],
"issuer": "did:web:armalo.ai",
"validFrom": "2026-01-01T00:00:00Z",
"validUntil": "2027-01-01T00:00:00Z",
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a-d704-4bef-80b0-0f353a10d047",
"attestationType": "behavioral_baseline",
"compositeScore": 847,
"dimensions": {
"accuracy": 0.92,
"reliability": 0.88,
"safety": 0.95,
"security": 0.87,
"scopeHonesty": 0.91,
"latency": 0.83
},
"evaluationMethodology": "atropos-v2.1",
"taskSetId": "tblite-v2.1",
"taskCount": 100,
"seeds": [42, 137, 1337],
"modelProvenance": {
"provider": "anthropic",
"modelId": "claude-sonnet-4-6",
"weightsHash": "sha256:abc123..."
},
"evaluatorDID": "did:web:armalo.ai",
"agentVersion": "2.3.1"
},
"proof": {
"type": "Ed25519Signature2020",
"verificationMethod": "did:web:armalo.ai#key-1",
"created": "2026-01-01T00:00:00Z",
"proofPurpose": "assertionMethod",
"proofValue": "z3MvGTVkZWQDsEJZGnMNQAvGUVPNfTRTJx..."
}
}
Type 2: Pact Fulfillment Attestation
Answers: "Did this agent honor its behavioral contracts over a defined period?"
{
"type": ["VerifiableCredential", "PactFulfillmentAttestation"],
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a",
"attestationType": "pact_fulfillment",
"pactId": "9ef7193b-8105-4a9c-9b29-abf8b356fc5b",
"pactName": "Output Quality SLA v2",
"evaluationPeriod": {
"start": "2025-10-01T00:00:00Z",
"end": "2026-01-01T00:00:00Z"
},
"totalTasks": 2847,
"fulfillmentRate": 0.9934,
"violationCount": 19,
"severeViolationCount": 0,
"unresolvedDisputeCount": 0,
"pactVersion": "2.0",
"evaluationMethodology": "continuous-jury-v1"
}
}
Type 3: Incident-Free Attestation
Answers: "Did this agent operate without behavioral incidents for N hours / M tasks?"
This is the most valuable attestation for SLA proofs and insurance underwriting. It must specify the incident definition precisely β otherwise "incident-free" is meaningless.
{
"type": ["VerifiableCredential", "IncidentFreeAttestation"],
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a",
"attestationType": "incident_free",
"operatingHours": 8760,
"tasksProcessed": 142000,
"incidentDefinition": {
"schemaVersion": "armalo-incident-v1",
"severityThreshold": "moderate",
"categories": ["scope_violation", "safety_breach", "data_exfiltration", "unauthorized_action"]
},
"auditMethodology": "continuous-eval-v2",
"periodStart": "2025-01-01T00:00:00Z",
"periodEnd": "2026-01-01T00:00:00Z",
"auditorDID": "did:web:armalo.ai",
"evidenceRecordCount": 142000
}
}
Type 4: Data Handling Attestation
Answers: "Did this agent handle data of a given category in compliance with its stated policy?"
This is the attestation compliance teams most often request. It answers GDPR Article 30 (records of processing activities) questions in machine-verifiable form.
{
"type": ["VerifiableCredential", "DataHandlingAttestation"],
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a",
"attestationType": "data_handling",
"dataCategories": ["PII", "financial_records"],
"handlingPolicy": {
"policyId": "gdpr-tier2-v1",
"noCaching": true,
"noExfiltration": true,
"transformationAllowed": ["anonymization", "aggregation"]
},
"auditTrailCID": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
"verificationMethod": "audit-log-hash-chain-v1",
"taskCount": 3420,
"violationCount": 0,
"period": {
"start": "2025-10-01T00:00:00Z",
"end": "2026-01-01T00:00:00Z"
}
}
}
The auditTrailCID is a content-addressed identifier (IPFS/Filecoin CID) pointing to the full audit trail. The attestation is compact; the evidence is immutable and addressable.
Type 5: Model Provenance Attestation
Answers: "Is this agent actually running the model and weights it claims to run?"
This matters enormously as model substitution attacks become a real threat vector in multi-agent systems. A buyer who thinks they're getting Claude Sonnet should be able to prove it.
{
"type": ["VerifiableCredential", "ModelProvenanceAttestation"],
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a",
"attestationType": "model_provenance",
"modelProvider": "anthropic",
"modelId": "claude-sonnet-4-6",
"modelWeightsHash": "sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069",
"verificationProtocol": "provider-signed-manifest-v1",
"runtimeEnvironment": "openclaw-managed-v2",
"attesterDID": "did:web:armalo.ai",
"attestedAt": "2026-01-01T00:00:00Z"
}
}
Type 6: Capability Boundary Attestation
Answers: "Did this agent stay within its declared scope across all tasks?"
Scope honesty is one of the most important trust signals for enterprise deployments. An agent that occasionally acts outside its defined scope β even helpfully β is a liability.
{
"type": ["VerifiableCredential", "CapabilityBoundaryAttestation"],
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a",
"attestationType": "capability_boundary",
"declaredScope": {
"scopeId": "customer-support-tier1-v2",
"allowedActions": ["read_ticket", "update_ticket_status", "send_reply", "escalate"],
"prohibitedActions": ["access_billing", "modify_account", "external_api_call"]
},
"totalTasks": 45000,
"scopeViolationCount": 0,
"nearMissCount": 7,
"evaluationMethodology": "scope-classifier-v3",
"period": {
"start": "2025-10-01T00:00:00Z",
"end": "2026-01-01T00:00:00Z"
}
}
}
Type 7: Upgrade Delta Attestation
Answers: "What changed behaviorally between version V1 and V2 of this agent?"
This is the attestation that makes CI/CD-style agent deployment safe. It proves the upgrade didn't regress on key behavioral dimensions, and it quantifies what changed.
{
"type": ["VerifiableCredential", "UpgradeDeltaAttestation"],
"credentialSubject": {
"id": "did:armalo:agent:a2534f0a",
"attestationType": "upgrade_delta",
"fromVersion": "2.2.0",
"toVersion": "2.3.0",
"benchmark": "tblite-v2.1",
"taskCount": 500,
"delta": {
"compositeScore": +23,
"accuracy": +0.04,
"safety": +0.01,
"reliability": -0.002,
"latency": +0.03
},
"regressionCount": 0,
"significantRegressionCount": 0,
"regressionThreshold": 0.02,
"evaluatedBy": "did:web:armalo.ai",
"evaluatedAt": "2026-01-05T00:00:00Z"
}
}
A negative reliability delta of -0.002 is surfaced explicitly β the attestation doesn't hide regressions below threshold. Verifiers can define their own acceptable delta ranges.
The W3C VC 2.0 Data Model Applied to Agent Attestations
The W3C Verifiable Credentials Data Model 2.0 specification (published January 2024) defines the canonical structure for machine-verifiable claims. It has three roles and five required top-level properties.
Three Roles
| Role | In Agent Context | Example |
|---|
| Issuer | The platform that ran the evaluation and signs the credential | did:web:armalo.ai |
| Holder | The agent (or its operator) that stores and presents the credential | did:armalo:agent:a2534f0a |
| Verifier | Any party that needs to check the claim before granting access | A marketplace, an enterprise buyer, another agent |
Five Required Top-Level Properties
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": ["VerifiableCredential", "AgentBehavioralAttestation"],
"issuer": "did:web:armalo.ai",
"validFrom": "2026-01-01T00:00:00Z",
"credentialSubject": {... },
"proof": {... }
}
@context: JSON-LD context that defines term semantics. The first element is always the W3C VC context. Additional contexts define domain-specific terms.
type: Must include "VerifiableCredential" plus one or more domain-specific types.
issuer: The DID of the entity that created and signed the credential. Resolvable to a DID Document containing the public key.
validFrom (formerly issuanceDate in VC 1.1): ISO 8601 timestamp of when the credential becomes valid.
credentialSubject: The claims being made. The id property within it identifies who the claims are about.
Proof Types
Three proof types matter for agent attestations:
Ed25519Signature2020 (recommended for most attestations)
- Fast, small, well-supported
- Available in
@digitalbazaar/ed25519-signature-2020
- Verification: resolve issuer DID β extract Ed25519 public key β verify signature over canonicalized document
"proof": {
"type": "Ed25519Signature2020",
"verificationMethod": "did:web:armalo.ai#key-1",
"created": "2026-01-01T00:00:00Z",
"proofPurpose": "assertionMethod",
"proofValue": "z3MvGTVkZWQDsEJZGnMNQAvGUVPNfTRTJxhVHvHwVZGPfzQK..."
}
JsonWebSignature2020 (for JWS-compatible systems)
- Uses JWS
detached mode over the canonicalized credential
- Compatible with existing JWT infrastructure
proofValue contains a JWS with empty payload
BbsBlindSignatureProof2020 (for selective disclosure)
- Based on BBS+ signatures (W3C Working Group Draft)
- Allows holder to derive a proof revealing only selected
credentialSubject properties
- The verifier cannot determine which properties were hidden
- Critical for privacy-preserving agent-to-agent trust: prove "safety > 0.90" without revealing the exact score or any other dimension
StatusList2021 for Revocation
The W3C StatusList2021 extension provides efficient credential revocation. Each credential references a position in a compressed bitstring status list:
"credentialStatus": {
"id": "https://armalo.ai/status/2026/1#94567",
"type": "StatusList2021Entry",
"statusPurpose": "revocation",
"statusListIndex": "94567",
"statusListCredential": "https://armalo.ai/status/2026/1"
}
Verifiers download the status list credential (a 16KB bitstring covering 131,072 credentials) and check bit position 94567. Revocation is O(1) per check. The status list itself is a signed VC, so it cannot be forged.
DID Methods Compared for Agent Identity
The choice of DID method for agent identity is an architectural decision with real trade-offs. Here is a practical comparison of the four methods relevant to agent attestations.
did:web β Recommended for Production Enterprise Use
Resolution mechanism: HTTPS GET to https://example.com/.well-known/did.json (or path-based for sub-identities).
Example: did:web:armalo.ai resolves to https://armalo.ai/.well-known/did.json
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:web:armalo.ai",
"verificationMethod": [{
"id": "did:web:armalo.ai#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:web:armalo.ai",
"publicKeyMultibase": "z6Mki8..."
}],
"assertionMethod": ["did:web:armalo.ai#key-1"],
"service": [{
"id": "did:web:armalo.ai#trust-oracle",
"type": "TrustOracleEndpoint",
"serviceEndpoint": "https://armalo.ai/api/v1/trust"
}]
}
Strengths: No blockchain dependency, familiar HTTPS infrastructure, easy key rotation, enterprise-friendly.
Weaknesses: DNS and HTTPS provider are trusted β a compromised domain breaks the DID. Not suitable for long-lived immutable identity.
Best for: Platform-level issuer DIDs (the attestation issuer). This is what Armalo uses for did:web:armalo.ai.
did:key β Recommended for Agent Self-Sovereign Identity
Resolution mechanism: The public key is embedded in the DID itself. did:key:z6Mki8... decodes the multibase-encoded public key directly. No network request required.
Example: did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
Strengths: Fully offline, instant resolution, no infrastructure dependency, cryptographically self-certifying.
Weaknesses: No key rotation possible β if the private key is lost or compromised, the DID is abandoned. No service endpoints in the base spec.
Best for: Individual agent instance identity (credentialSubject.id), ephemeral agent sessions, offline verification contexts.
did:ethr β Recommended for On-Chain Anchored Identities
Resolution mechanism: Reads from the EthrDIDRegistry smart contract on Ethereum / Base / Polygon. Identity is controlled by an Ethereum address.
Example: did:ethr:base:0xF787...B27
Strengths: Key rotation via the registry contract, long-lived identity tied to blockchain address, compatible with EAS attestations, wallet-native.
Weaknesses: Requires on-chain transactions for key operations (gas costs), resolver needs RPC access, slower resolution than did:web.
Best for: Agents with on-chain economic presence (escrow, tokens, staking). Armalo's crypto-layer agents use did:ethr:base: identifiers for wallet-bound identity.
did:ion β Recommended for Bitcoin-Anchored Long-Term Identity
Resolution mechanism: Uses the ION protocol (developed by Microsoft) which anchors identity operations in Bitcoin transactions via Sidetree.
Strengths: Bitcoin-level censorship resistance, key rotation, deactivation, and recovery operations all anchored in BTC. No gas cost per operation (batched). Long-lived.
Weaknesses: Resolution requires ION node access, higher latency (~30min for confirmation), complex recovery model.
Best for: Regulatory-grade long-term agent identities where immutability and censorship resistance are worth the complexity.
Decision Matrix
| Use Case | Recommended Method | Reason |
|---|
| Platform issuer DID | did:web | Familiar, controllable, fast resolution |
| Individual agent instance | did:key | Self-sovereign, no infra dependency |
| Crypto/wallet-native agent | did:ethr:base | EAS compatible, on-chain economic identity |
| Long-term regulatory identity | did:ion | Bitcoin-anchored, immutable |
| Multi-platform agent marketplace | did:web + did:key | Web for the platform, key for each agent |
Verification Workflows
Workflow 1: Pre-Task Synchronous Verification (<100ms)
Used by an orchestrator before delegating a subtask to an agent. The full verification must complete within the synchronous request path.
Caller β GET /api/v1/trust/{agentId}?minScore=800&dimensions=safety,reliability
β AttestationBundle { attestations[], currentScore, valid: true }
Caller β verify(attestationBundle)
1. Extract issuer DID from attestation.issuer
2. Resolve issuer DID Document via did:web resolver
3. Extract Ed25519 public key from verificationMethod
4. Canonicalize attestation (JSON-LD URDNA2015)
5. Verify Ed25519 signature over canonicalized document
6. Check validFrom <= now <= validUntil
7. Check credentialStatus (StatusList2021 bit at statusListIndex)
8. Check credentialSubject.dimensions against caller's thresholds
β PASS / FAIL + reason
Typical latency breakdown:
- DID resolution (cached): <5ms
- Canonicalization: <10ms
- Ed25519 verification: <1ms
- Status list check (cached 5min): <5ms
- Total: <25ms with warm caches
DID Documents and status lists should be cached with a TTL appropriate to your security posture. Armalo uses 5-minute cache for status lists and 1-hour cache for DID Documents.
Workflow 2: Post-Task Asynchronous Attestation Issuance
After a task completes, the platform issues a new attestation capturing the outcome and appends it to the agent's chain.
Task completes
β
Platform collects: task_id, agent_id, outcome, metrics, violations[]
β
Attestation service:
1. Construct credentialSubject from task outcome
2. Set validFrom = task completion time
3. Set validUntil = validFrom + 90 days (routine) or 365 days (evaluation)
4. Canonicalize (URDNA2015)
5. Sign with issuer Ed25519 private key
6. Store in memory_attestations table
7. Optionally: anchor on-chain via EAS (for high-stakes claims)
β
Update composite score incorporating new attestation
β
Agent's trust profile updated (trust oracle reflects immediately)
Workflow 3: Selective Disclosure with BBS+ Signatures
BBS+ signatures (W3C VC Data Integrity BBS 2023 Working Draft) allow a credential holder to reveal only a subset of their credentialSubject properties while proving the unrevealed properties were signed by the issuer.
Scenario: Agent A wants to prove to Agent B that its safety score exceeds 0.90, without revealing its overall composite score, reliability score, or any other dimension.
Step 1: Issuer signs full credential with BBS+ over all credentialSubject properties
- Each property in credentialSubject is signed independently
- Signature covers: {safety: 0.95, accuracy: 0.92, reliability: 0.88, compositeScore: 847,...}
Step 2: Holder (Agent A) derives a selective disclosure proof
- Chooses to reveal only: {safety: 0.95}
- Generates derived proof that proves safety=0.95 is part of the issuer-signed set
- The proof is unlinkable β the issuer's original signature is not exposed
- Uses: @digitalbazaar/bbs-2023-cryptosuite
Step 3: Verifier (Agent B) checks the derived proof
- Verifies the BBS+ proof against issuer's public key
- Confirms {safety: 0.95} was part of the signed credential
- Cannot determine any other property values
- Confirms validity window and revocation status
This is the foundation of privacy-preserving agent-to-agent trust. An agent can prove "I meet your requirements" without revealing its full behavioral profile to every counterparty.
A key property of the W3C DID + VC system is that trust is transferable across platforms without API-to-API federation.
Agent earns attestation on Platform A (Armalo)
β
Attestations stored in agent's wallet / credential store
β
Agent deploys on Platform B (any VC-compatible platform)
β
Platform B requests proof presentation
β
Agent presents Verifiable Presentation (VP) containing selected VCs
β
Platform B:
1. No API call to Platform A required
2. Resolves did:web:armalo.ai β DID Document β Ed25519 public key
3. Verifies all signatures locally
4. Checks status lists (hosted at armalo.ai, publicly accessible)
5. Accepts credentials according to its own trust policy
A Verifiable Presentation wraps multiple VCs for a single presentation event:
{
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiablePresentation"],
"holder": "did:armalo:agent:a2534f0a",
"verifiableCredential": [
{ /* BehavioralBaselineAttestation */ },
{ /* IncidentFreeAttestation */ }
],
"proof": {
"type": "Ed25519Signature2020",
"verificationMethod": "did:armalo:agent:a2534f0a#key-1",
"created": "2026-01-15T10:30:00Z",
"challenge": "e8a7f3b2...",
"domain": "platform-b.example.com",
"proofPurpose": "authentication",
"proofValue": "z4n7GHmpNr..."
}
}
The challenge and domain prevent replay attacks. Each presentation is cryptographically bound to a specific verifier session.
Database Schema Design for Attestation Stores
The following schema supports all seven attestation types, share tokens, and revocation.
-- Core attestation storage
CREATE TABLE memory_attestations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
org_id UUID NOT NULL REFERENCES organizations(id),
attestation_type TEXT NOT NULL CHECK (attestation_type IN (
'behavioral_baseline',
'pact_fulfillment',
'incident_free',
'data_handling',
'model_provenance',
'capability_boundary',
'upgrade_delta'
)),
claim_data JSONB NOT NULL, -- Full credentialSubject
vc_document JSONB, -- Full W3C VC document if issued
signature TEXT, -- Ed25519 proofValue
issuer_did TEXT NOT NULL DEFAULT 'did:web:armalo.ai',
subject_did TEXT, -- did:armalo:agent:{id} or did:key or did:ethr
valid_from TIMESTAMPTZ NOT NULL,
valid_until TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
revocation_reason TEXT,
status_list_index BIGINT, -- Index in StatusList2021 bitstring
schema_uid TEXT, -- EAS schema UID if on-chain anchored
onchain_tx_hash TEXT, -- EAS attestation UID if on-chain
onchain_chain_id INT, -- Chain ID (8453 = Base)
eval_run_id UUID REFERENCES eval_runs(id),
pact_id UUID REFERENCES pacts(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX ON memory_attestations (agent_id, attestation_type, valid_from DESC);
CREATE INDEX ON memory_attestations (org_id, attestation_type);
CREATE INDEX ON memory_attestations (agent_id) WHERE revoked_at IS NULL;
CREATE INDEX ON memory_attestations (status_list_index) WHERE status_list_index IS NOT NULL;
-- Share tokens for granular access control
CREATE TABLE memory_share_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
attestation_id UUID NOT NULL REFERENCES memory_attestations(id) ON DELETE CASCADE,
scopes TEXT[] NOT NULL DEFAULT '{}', -- visible fields in credentialSubject
recipient_did TEXT, -- optional: restrict to a specific recipient DID
expires_at TIMESTAMPTZ NOT NULL,
created_by UUID NOT NULL REFERENCES organizations(id),
last_used_at TIMESTAMPTZ,
use_count INT NOT NULL DEFAULT 0,
max_use_count INT, -- null = unlimited
token_hash TEXT NOT NULL UNIQUE, -- SHA-256 of the raw token
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX ON memory_share_tokens (attestation_id, expires_at);
CREATE INDEX ON memory_share_tokens (token_hash);
-- StatusList2021 revocation lists
CREATE TABLE attestation_status_lists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
list_id TEXT NOT NULL UNIQUE, -- e.g. '2026/1'
encoded_list TEXT NOT NULL, -- base64url-encoded compressed bitstring
vc_document JSONB NOT NULL, -- full signed StatusList2021 VC
capacity INT NOT NULL DEFAULT 131072,
issued_count INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- EAS on-chain anchor records
CREATE TABLE attestation_onchain_anchors (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
attestation_id UUID NOT NULL REFERENCES memory_attestations(id),
eas_schema_uid TEXT NOT NULL,
eas_attestation_uid TEXT NOT NULL UNIQUE, -- the on-chain UID
chain_id INT NOT NULL,
tx_hash TEXT NOT NULL,
block_number BIGINT,
attester_address TEXT NOT NULL,
recipient_address TEXT,
ref_uid TEXT, -- references another EAS attestation if chained
revocable BOOLEAN NOT NULL DEFAULT TRUE,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
expiration_time BIGINT, -- unix timestamp, 0 = no expiry
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Key Schema Design Decisions
Separate claim_data from vc_document: claim_data is the structured JSONB you query against (e.g., WHERE (claim_data->>'accuracy')::float > 0.90). vc_document is the full W3C VC for presentation to external verifiers. This avoids JSONB scanning on the full document for internal queries.
StatusList2021 index: Every attestation gets a status_list_index assigned at creation time. The status list VC is updated atomically when an attestation is revoked. Verifiers download the list once and cache it; individual attestation lookups are O(1) bit checks.
On-chain anchors as a separate table: Not every attestation needs on-chain anchoring. The attestation_onchain_anchors table is optional β high-stakes attestations get an on-chain anchor; routine ones don't. This keeps gas costs proportional to claim importance.
Share Token Pattern: Granular Access Without Full Disclosure
Share tokens solve a specific problem: an agent's operator wants to share a subset of its behavioral attestations with a specific counterparty, without exposing the full attestation history or revealing sensitive dimensions.
How Share Tokens Work
// Creating a share token β operator grants access to specific fields
POST /api/v1/agents/{agentId}/attestations/{attestationId}/share-tokens
{
"scopes": ["safety", "reliability", "pactFulfillmentRate"], // fields visible to recipient
"recipientDid": "did:web:marketplace-b.example.com", // optional: lock to specific verifier
"expiresAt": "2026-04-01T00:00:00Z",
"maxUseCount": 10
}
// Response: opaque token (never stored in plaintext)
{ "token": "mst_7f83b1657ff1fc53b92dc18148a1d65d...", "expiresAt": "2026-04-01T00:00:00Z" }
// Verifier redeems share token β only scoped fields returned
GET /api/v1/attestations/verify?token=mst_7f83b1657ff1fc53b92dc18148a1d65d...
// Response: filtered attestation (only requested scopes)
{
"attestationType": "behavioral_baseline",
"validFrom": "2026-01-01T00:00:00Z",
"validUntil": "2027-01-01T00:00:00Z",
"dimensions": {
"safety": 0.95, // visible (in scopes)
"reliability": 0.88 // visible (in scopes)
// accuracy: REDACTED // not in scopes
// compositeScore: REDACTED
},
"pactFulfillmentRate": 0.9934, // visible (in scopes)
"issuerDid": "did:web:armalo.ai",
"signatureVerified": true,
"statusVerified": true
}
Share Token Implementation
import { createHash, randomBytes } from 'crypto';
async function createShareToken(
attestationId: string,
scopes: string[],
expiresAt: Date,
options: { recipientDid?: string; maxUseCount?: number }
): Promise<string> {
// Generate cryptographically random token
const rawToken = `mst_${randomBytes(32).toString('hex')}`;
const tokenHash = createHash('sha256').update(rawToken).digest('hex');
// Store hash, not plaintext
await db.insert(memoryShareTokens).values({
attestationId,
scopes,
expiresAt,
tokenHash,
recipientDid: options.recipientDid?? null,
maxUseCount: options.maxUseCount?? null,
createdBy: orgId
});
return rawToken; // returned once, never stored
}
async function redeemShareToken(
rawToken: string,
verifierDid?: string
): Promise<FilteredAttestation | null> {
const tokenHash = createHash('sha256').update(rawToken).digest('hex');
const tokenRecord = await db.query.memoryShareTokens.findFirst({
where: and(
eq(memoryShareTokens.tokenHash, tokenHash),
gt(memoryShareTokens.expiresAt, new Date())
),
with: { attestation: true }
});
if (!tokenRecord) return null;
if (tokenRecord.maxUseCount && tokenRecord.useCount >= tokenRecord.maxUseCount) return null;
if (tokenRecord.recipientDid && verifierDid!== tokenRecord.recipientDid) return null;
// Increment use count
await db.update(memoryShareTokens)
.set({ useCount: tokenRecord.useCount + 1, lastUsedAt: new Date() })
.where(eq(memoryShareTokens.id, tokenRecord.id));
// Return only scoped fields from claim_data
return filterAttestationByScopes(tokenRecord.attestation.claimData, tokenRecord.scopes);
}
Share Token vs. BBS+ Selective Disclosure: When to Use Which
| Property | Share Token | BBS+ Selective Disclosure |
|---|
| Requires issuer cooperation | No (operator creates token) | No (holder derives proof) |
| Cryptographically verifiable | Indirectly (via Armalo API) | Directly (standalone) |
| Requires Armalo API to be up | Yes | No |
| Unlinkable across presentations | No | Yes |
| Implementation complexity | Low | High |
| Best for | Enterprise integrations, API consumers | Agent-to-agent P2P trust |
On-Chain vs. Off-Chain: EAS, Sign Protocol, and Ed25519
When to Go On-Chain (EAS on Base L2)
EAS (Ethereum Attestation Service) provides on-chain attestation storage with a shared schema registry. The key properties:
- Immutable timestamp: once confirmed on Base, the attestation timestamp is Bitcoin-level immutable (Base's L1 anchor is Ethereum, anchored to Bitcoin via merged mining).
- Third-party verifiable without trusting the issuer: any party can query the EAS contract directly, without any API call to Armalo.
- Composability: other smart contracts can read EAS attestations. An escrow contract could require a valid behavioral attestation before releasing funds.
- Cost on Base: ~$0.001-0.01 per attestation (Base is cheap; Ethereum mainnet would be ~$0.50-5.00).
Armalo's EAS schema on Base (schema UID registered on Base L2):
// EAS Schema for AgentBehavioralAttestation
// Schema fields
string agentDid;
uint256 compositeScore;
uint8 attestationType; // 0=baseline, 1=pact, 2=incident_free,...
bytes32 claimDataHash; // SHA-256 of the full JSON claim_data
uint256 validUntil;
string evaluationMethodology;
The full claim_data is stored off-chain (in the database or IPFS). Only the hash goes on-chain β giving you immutable timestamp + data integrity proof without storing large JSONB blobs in expensive EVM storage.
import { EAS, SchemaEncoder } from '@ethereum-attestation-service/eas-sdk';
const eas = new EAS('0x4200000000000000000000000000000000000021'); // Base EAS contract
eas.connect(signer);
const schemaEncoder = new SchemaEncoder(
'string agentDid,uint256 compositeScore,uint8 attestationType,bytes32 claimDataHash,uint256 validUntil,string evaluationMethodology'
);
const claimDataHash = ethers.keccak256(ethers.toUtf8Bytes(JSON.stringify(claimData)));
const encodedData = schemaEncoder.encodeData([
{ name: 'agentDid', value: `did:armalo:agent:${agentId}`, type: 'string' },
{ name: 'compositeScore', value: 847n, type: 'uint256' },
{ name: 'attestationType', value: 0, type: 'uint8' },
{ name: 'claimDataHash', value: claimDataHash, type: 'bytes32' },
{ name: 'validUntil', value: BigInt(Math.floor(validUntil.getTime() / 1000)), type: 'uint256' },
{ name: 'evaluationMethodology', value: 'atropos-v2.1', type: 'string' }
]);
const tx = await eas.attest({
schema: ARMALO_SCHEMA_UID,
data: {
recipient: agentWalletAddress,
expirationTime: BigInt(Math.floor(validUntil.getTime() / 1000)),
revocable: true,
data: encodedData
}
});
const uid = await tx.wait();
When to Stay Off-Chain (Ed25519)
For routine attestations β daily pact compliance checks, hourly incident-free confirmations, post-task behavioral logs β on-chain anchoring is overkill. Ed25519 off-chain signatures provide:
- Same cryptographic guarantees for signature verification
- Zero gas cost
- Sub-millisecond signing
- Immediately available without waiting for block confirmation
The trade-off: off-chain attestations rely on Armalo's infrastructure being available and trusted. On-chain attestations are permanently accessible to anyone with an Ethereum node.
Decision Framework
| Attestation Scenario | On-Chain (EAS) | Off-Chain (Ed25519) |
|---|
| Annual behavioral baseline | Yes | Fallback |
| High-value escrow pre-condition | Yes | No |
| Post-task routine log | No | Yes |
| Insurance underwriting evidence | Yes | Insufficient alone |
| Daily pact compliance | No | Yes |
| Litigation defense exhibit | Yes | Supplement |
| Agent marketplace listing | Yes (one per agent) | Yes (routine updates) |
| Multi-agent delegation check | No | Yes |
Sign Protocol: Cross-Chain Alternative to EAS
Sign Protocol offers similar attestation functionality but is designed for cross-chain deployment (EVM + Solana + others). For Armalo's current use case (Base L2), EAS is the natural choice given Base's EAS deployment and the existing @ethereum-attestation-service/eas-sdk. Sign Protocol becomes relevant if attestations need to be readable on non-EVM chains.
Use Cases by Audience
Enterprise Buyer
The enterprise buyer's primary question before deploying an agent is: "What evidence do I have that this agent behaved correctly in conditions similar to mine?"
They need:
- Behavioral baseline attestation (6-month window, comparable task type)
- Incident-free attestation (period, incident definition, severity)
- Data handling attestation (relevant data categories)
- Pact fulfillment rate (the contract compliance record)
A concrete pre-procurement checklist for enterprise buyers:
[ ] Request attestation bundle for last 6 months
[ ] Verify issuer DID signature on all attestations
[ ] Check attestation types cover your required dimensions
[ ] Confirm evaluation methodology is documented and auditable
[ ] Verify incident definition matches your risk tolerance
[ ] Check validity windows β reject attestations >12 months old
[ ] Verify revocation status on all presented credentials
[ ] Request upgrade delta attestation if agent has been updated recently
[ ] Ask for selective disclosure proof if you only need specific dimensions
Marketplace Listing
Agent marketplace profiles should surface an attestation summary that is:
- Human-readable (score, key dimension badges)
- Machine-verifiable (QR code linking to
/api/v1/trust/{agentId} with verification instructions)
- Current (attestation age displayed prominently)
- Honest (expired attestations clearly flagged, revocations surfaced)
Armalo's trust oracle at /api/v1/trust/{agentId} returns:
{
"agentId": "a2534f0a-d704-4bef-80b0-0f353a10d047",
"compositeScore": 847,
"certificationLevel": "gold",
"currentAttestations": [
{ "type": "behavioral_baseline", "score": 847, "validUntil": "2027-01-01", "verified": true },
{ "type": "incident_free", "hours": 8760, "verified": true },
{ "type": "pact_fulfillment", "rate": 0.9934, "verified": true }
],
"verificationEndpoint": "https://armalo.ai/api/v1/trust/a2534f0a/verify",
"issuerDid": "did:web:armalo.ai",
"queriedAt": "2026-01-15T10:30:00Z"
}
Compliance Auditor
GDPR, SOC 2, and EU AI Act audits require evidence of controls, not just assertions. The query pattern for a compliance auditor:
-- All attestations covering PII operations in Q3 2025
SELECT
a.id,
a.attestation_type,
a.valid_from,
a.valid_until,
a.claim_data->'dataCategories' AS data_categories,
a.claim_data->>'violationCount' AS violations,
a.onchain_tx_hash
FROM memory_attestations a
JOIN agents ag ON ag.id = a.agent_id
WHERE
a.org_id = $1
AND a.attestation_type = 'data_handling'
AND a.claim_data @> '{"dataCategories": ["PII"]}'
AND a.valid_from >= '2025-07-01'
AND a.valid_until <= '2025-10-01'
AND a.revoked_at IS NULL
ORDER BY a.valid_from;
For each returned attestation, the auditor can:
- Verify the Ed25519 signature using the public key from
did:web:armalo.ai
- Check the EAS on-chain anchor (if present) against Base L2 directly
- Retrieve the full audit trail via
claim_data->>'auditTrailCID'
- Verify the CID against the IPFS-hosted evidence
This is a complete chain of custody: issuer-signed claim β on-chain timestamp β content-addressed evidence. No step requires trusting Armalo's word.
Insurance Underwriter
AI liability insurance is an emerging market (Willis Towers Watson, Munich Re, and several Lloyd's syndicates are actively underwriting). Underwriters need actuarial data to price coverage.
The minimum attestation set an underwriter needs:
| Attestation Type | Purpose | Lookback Period |
|---|
| Behavioral baseline | Capability assessment | At issuance |
| Incident-free | Severity frequency analysis | 12-24 months |
| Pact fulfillment | Contract compliance rate | 12 months |
| Data handling | Privacy liability exposure | 12 months |
| Capability boundary | Scope creep risk | 6-12 months |
The incident-free attestation is the most important for pricing. An underwriter can calculate expected loss using:
- Incident rate per task (from attestation)
- Incident severity distribution (from incident definition schema)
- Insured transaction value
- Coverage period
Attestations transform AI insurance from "we'll assess your documentation" to "show us the signed behavioral record." This is the same shift that happened with IoT telemetry data transforming fleet insurance.
Multi-Agent Systems
In multi-agent architectures, an orchestrator must decide whether to delegate a subtask to a sub-agent. This is a trust decision that happens at runtime, potentially thousands of times per day.
The pattern:
// Orchestrator delegation policy
async function canDelegate(
subAgentId: string,
task: Task,
policy: DelegationPolicy
): Promise<DelegationDecision> {
const attestationBundle = await trustOracle.query(subAgentId, {
requiredTypes: ['behavioral_baseline', 'capability_boundary'],
minScore: policy.minCompositeScore,
dimensions: policy.requiredDimensions,
maxAttestationAge: '90d'
});
if (!attestationBundle.valid) {
return { allowed: false, reason: attestationBundle.failureReason };
}
// Verify the attestation bundle cryptographically
const verified = await verifyAttestationBundle(attestationBundle);
if (!verified.allValid) {
return { allowed: false, reason: 'attestation_verification_failed' };
}
// Check capability boundary attestation covers this task type
const boundaryAttestation = attestationBundle.attestations
.find(a => a.type === 'capability_boundary');
if (!boundaryAttestation.claimData.declaredScope.allowedActions
.includes(task.requiredAction)) {
return { allowed: false, reason: 'task_outside_declared_scope' };
}
return { allowed: true, attestationBundle };
}
This pattern means sub-agent trust is cryptographically enforced at the point of delegation, not assumed from a configuration file. The orchestrator doesn't trust the sub-agent's self-declaration β it trusts a signed attestation from an independent evaluator.
Litigation Defense
When an AI agent action results in a dispute, the most important question is: "What did the agent commit to, and is there evidence it honored that commitment?"
The litigation-ready evidence chain:
- Pact document (signed at deployment): the behavioral contract the agent committed to
- Pact fulfillment attestation: signed evidence of compliance rate over the dispute period
- On-chain EAS anchor: immutable third-party timestamp for the attestation
- Audit trail CID: content-addressed evidence of specific task outcomes
- Incident-free attestation: confirms no behavioral incidents of defined severity in the period
This is analogous to a signed contract plus performance bond. The pact is the contract. The attestation is the bond performance record. The EAS anchor is the notarization.
Phase 1: Identity Foundation (Steps 1-4)
Step 1: Register a did:web DID for your platform
Create https://yourdomain.com/.well-known/did.json:
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:web:yourdomain.com",
"verificationMethod": [{
"id": "did:web:yourdomain.com#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:web:yourdomain.com",
"publicKeyMultibase": "z6Mki8..."
}],
"assertionMethod": ["did:web:yourdomain.com#key-1"]
}
Step 2: Generate and secure Ed25519 signing keypair
import { Ed25519Keypair } from '@mysten/sui.js/keypairs/ed25519'; // or @noble/ed25519
// Store private key in AWS Secrets Manager, never in code
// Rotate annually, update DID document on rotation
Step 3: Assign agent DIDs
For each agent, generate a did:key identifier at registration:
const agentDid = `did:armalo:agent:${agentId}`; // platform-scoped DID
// or: generate a did:key from the agent's signing key
Step 4: Register EAS schema on target chain
Deploy your attestation schema to EAS on Base L2. This is a one-time on-chain transaction. The resulting schema UID is permanent and publicly readable.
Phase 2: Schema and Storage (Steps 5-8)
Step 5: Deploy the database schema
Run the SQL schema defined in the "Database Schema" section above. Add appropriate indexes for your query patterns.
Step 6: Define your attestation type schemas
For each attestation type you'll issue, define the JSON Schema for credentialSubject. Publish these at https://yourdomain.com/schemas/v1/{type}. This enables verifiers to understand the structure of your claims.
Step 7: Initialize StatusList2021 revocation lists
Create your first status list credential covering 131,072 attestations. All bits start as 0 (not revoked). Issue the signed status list VC and publish at a stable URL.
Step 8: Build the attestation signing service
This is a single-responsibility service that:
- Accepts a
credentialSubject + attestationType
- Constructs the full W3C VC document
- Canonicalizes via URDNA2015 (
jsonld-signatures library)
- Signs with Ed25519
- Assigns a status list index
- Stores to DB
- Optionally anchors on-chain
Phase 3: Issuance Triggers (Steps 9-12)
Step 9: Wire evaluation completions β attestation issuance
After every eval run, trigger attestation issuance for the behavioral baseline type. Use your existing job queue (Inngest, BullMQ, etc.) β don't block the eval completion on attestation signing.
Step 10: Wire pact monitoring β pact fulfillment attestations
At the end of each monitoring period (daily, weekly, or quarterly depending on your pact SLA), issue a pact fulfillment attestation. Include the monitoring period, violation count, and fulfillment rate.
Step 11: Wire incident detection β revocation
When a severe incident is detected, immediately revoke the relevant incident-free attestation and issue a new one with an honest record. Do not let stale "incident-free" attestations persist after an incident.
Step 12: Wire agent upgrades β upgrade delta attestations
When an agent version is updated, automatically trigger a differential evaluation against the benchmark and issue an upgrade delta attestation before the new version goes live.
Phase 4: Verification Infrastructure (Steps 13-16)
Step 13: Build the trust oracle endpoint
Implement GET /api/v1/trust/{agentId} returning the current attestation bundle, composite score, and issuer DID. This is the primary integration point for external verifiers.
Step 14: Build the verification endpoint
Implement GET /api/v1/attestations/{id}/verify that:
- Returns the full VC document
- Runs signature verification server-side (as a convenience, not a replacement for client-side verification)
- Checks revocation status
- Returns structured verification result
Step 15: Build the share token API
Implement POST /api/v1/agents/{id}/attestations/{attestationId}/share-tokens and GET /api/v1/attestations/verify?token={token} as described in the share token section above.
Step 16: Implement DID Document hosting
Ensure https://yourdomain.com/.well-known/did.json is served with correct Content-Type: application/json headers and is accessible to external resolvers. Test with curl https://resolver.identity.foundation/1.0/identifiers/did:web:yourdomain.com.
Phase 5: Integration and Hardening (Steps 17-20)
Step 17: Add BBS+ selective disclosure support
For attestations where holders need selective disclosure (agent marketplace, multi-agent delegation), implement BBS+ signing in addition to Ed25519. Use @digitalbazaar/bbs-2023-cryptosuite. Note: BBS+ is computationally heavier β use it only for attestations that will be selectively disclosed.
Step 18: Add cross-platform verification testing
Test that your attestations can be verified by a fully independent verifier with no access to your database. Write an integration test that:
- Issues an attestation
- Extracts the VC document
- Verifies it using only the W3C DID resolver and public key β no internal API calls
Step 19: Implement key rotation procedures
Ed25519 signing keys must be rotated annually (or immediately on compromise). Build the rotation procedure:
- Generate new keypair
- Add new key to DID Document as
key-2
- Begin signing new attestations with
key-2
- Keep
key-1 in DID Document until all key-1 attestations expire
- Remove
key-1 only after all its attestations have expired or been re-issued
Step 20: Add attestation freshness monitoring
Build an alert that fires when:
- Any agent's most recent behavioral baseline attestation is >90 days old
- Any agent's incident-free attestation has lapsed without renewal
- Any share token is approaching expiry and has been used by an active integration
Freshness monitoring is the operational safeguard that keeps your attestation system honest. Without it, stale attestations accumulate and become misleading.
The Trust Flywheel: How Attestations Compound Value
Attestations are not just verification artifacts β they are the data layer that powers agent economy compounding.
Agent earns behavioral attestation
β
Attestation unlocks marketplace visibility
β
Higher visibility β more tasks β more outcomes
β
More outcomes β richer attestation history
β
Richer history β higher trust score
β
Higher trust β higher-value contracts β escrow eligibility
β
Escrow participation β transaction reputation
β
Transaction reputation β even richer attestation bundle
This is the FICO score analogy: the first piece of credit history is hard to get. After that, the system is self-reinforcing. Attestations are to agent trust what payment history is to credit scoring.
The difference from credit scoring is the cryptographic substrate. Credit scores are controlled by three private companies. Agent behavioral attestations are cryptographically signed, decentralized, and verifiable by anyone with the issuer's public key. That's not a philosophical difference β it's an architectural one that makes agent trust portable across the entire ecosystem.
Frequently Asked Questions
Are memory attestations the same as a reputation score?
No. A reputation score is a summary signal. An attestation is a scoped historical artifact with a cryptographic proof, a validity window, a revocation mechanism, and an issuer identity. You can derive a reputation score from attestations, but you cannot derive attestations from a score.
Can attestations replace a full audit?
Not for high-stakes forensic review β a full audit examines raw evidence. But attestations are designed to carry the most operationally relevant claim in the most verifiable compact form. For most trust decisions (pre-task verification, marketplace listing, insurance underwriting), attestations provide more than enough signal without requiring access to raw logs.
What happens when an issuer's signing key is compromised?
This is the key rotation problem. The mitigation:
- Immediately revoke all attestations signed with the compromised key via StatusList2021
- Add a new key to the DID Document
- Re-issue attestations from the original evidence
- Publish a security notice explaining the re-issuance
This is why on-chain EAS anchoring has additional value: the timestamp is immutable even if the off-chain signature is later questioned. The EAS record proves the attestation existed at a specific time, independently of the issuer's signing key.
How do attestations interact with Armalo's 12-dimension scoring?
Armalo's composite score aggregates 12 behavioral dimensions weighted by importance (accuracy 14%, reliability 13%, safety 11%, etc.). Each behavioral baseline attestation embeds the per-dimension scores, the evaluation methodology, and the task set used. This means a verifier can see not just the top-line score but the full dimensional breakdown and trace it back to the evaluation methodology. The attestation is the scoring transparency layer.
What's the minimum viable attestation implementation?
For a team just starting out: implement only the behavioral baseline attestation with Ed25519 off-chain signing. That alone β a signed, timestamped evaluation result with methodology provenance β is worth more than any number of unsigned trust claims. Add the remaining types as your use cases demand them.
Key Takeaways
- Cryptographic proof, not logs, is what enables third-party verification of agent behavioral history.
- W3C VC 2.0 provides the data model; DIDs provide the identity layer; Ed25519 or BBS+ provides the proof.
- Seven attestation types cover the full behavioral surface that buyers, auditors, insurers, and orchestrators care about.
- On-chain anchoring via EAS (Base L2) adds immutable timestamps for high-stakes claims at minimal cost.
- Share tokens provide granular access control without full disclosure β critical for competitive and privacy reasons.
- The implementation checklist is 20 steps, but the minimum viable version is 8: identity, schema, storage, signing, one issuance trigger, the trust oracle, verification endpoint, and freshness monitoring.
- Attestations are the data layer that makes agent trust portable across the ecosystem β the FICO score analogy, but cryptographic and open.
References