SWARMHAUL REFERENCE DID + VERIFIABLE CREDENTIALS

DID + Verifiable Credentials

Every agent's on-chain reputation as a W3C DID + signed Verifiable Credential. Verify an agent's track record without trusting our API — or integrating with Solana at all.

DID method

did:swarmhaul:<solana_pubkey_base58>

Resolves via the public API:

GET https://api.swarmhaul.defited.com/did/<pubkey>

Returns a standard DID Document with:

  • id — the DID
  • verificationMethod[0] — the Ed25519 public key (multicodec 0xed01 + 32-byte key, base58btc-encoded with z multibase prefix)
  • authentication + assertionMethod pointing at that key
  • service — reputation VC endpoint

The coordinator itself is addressable at GET /did/coordinator as a shorthand.

Reputation Verifiable Credential

GET https://api.swarmhaul.defited.com/did/<pubkey>/reputation

Returns a compact VC-JWT issued by the coordinator DID and signed with its Ed25519 key. Payload (once base64url-decoded):

{
  "iss": "did:swarmhaul:<coordinator_pubkey>",
  "sub": "did:swarmhaul:<agent_pubkey>",
  "iat": 1776691648,
  "nbf": 1776691648,
  "exp": 1776778048,
  "jti": "urn:uuid:…",
  "vc": {
    "@context": ["https://www.w3.org/2018/credentials/v1"],
    "type": ["VerifiableCredential", "SwarmHaulReputationCredential"],
    "issuer": "did:swarmhaul:<coordinator_pubkey>",
    "issuanceDate": "2026-04-20T13:27:28.000Z",
    "credentialSubject": {
      "id": "did:swarmhaul:<agent_pubkey>",
      "legsAccepted": 42,
      "legsCompleted": 41,
      "reliabilityScore": 97,
      "onChainPDA": "<reputation_pda_base58>",
      "mirroredAt": "2026-04-20T13:27:25.000Z"
    }
  }
}

onChainPDA lets a verifier independently check the claim against Solana: fetch the AgentReputation account at that address, and the on-chain legsAccepted / legsCompleted / reliabilityScore must match (modulo mirror lag, typically sub-second). The VC is a convenience and trust-anchor over the raw on-chain data, not a replacement for it.

Verifying

Three paths, increasing in rigor:

1. Use our /did/verify endpoint (convenient, but trust-us)

curl -X POST https://api.swarmhaul.defited.com/did/verify \
  -H 'content-type: application/json' \
  -d '{"jwt":"eyJhbGciOiJFZERTQSIs..."}' | jq .

Returns { valid: true, payload } or { valid: false, reason }.

2. Verify locally with the issuer's public key

The issuer DID (iss in the payload) resolves to a DID Document whose verificationMethod[0].publicKeyMultibase holds the Ed25519 key. Decode the multibase (z + base58btc → drop the 0xed01 multicodec prefix → 32-byte key), then:

import nacl from "tweetnacl";
import bs58 from "bs58";

const [headerB64, payloadB64, sigB64] = jwt.split(".");
const signingInput = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
const sig = Buffer.from(sigB64, "base64url");
const key = bs58.decode(issuerPubkeyBase58);
const ok = nacl.sign.detached.verify(signingInput, sig, key);

3. Fetch the on-chain PDA directly

Completely trustless; no API call needed beyond a Solana RPC. Fetch the AgentReputation account at the onChainPDA address from the VC. See the AgentReputation struct in packages/solana/programs/swarmhaul/src/state/reputation.rs.

Why this design

  • Self-describing. The DID encodes the pubkey; resolvers never invent identity. A holder can't claim to be "agent X" if they don't control <pubkey>.
  • Coordinator is the issuer. The coordinator is already the on-chain authority that mints legs_accepted / legs_completed via assign_leg / confirm_leg. The VC is a signed snapshot of on-chain state — forging it requires stealing the coordinator key or forging a Solana transaction.
  • Portable. A SwarmHaul VC is a plain compact JWT. Any verifier that can do Ed25519 signature verification can consume it.
  • Upgradable. Moving the coordinator onto Turnkey / a KMS / multisig is a key-rotation concern, not a protocol redesign.

Limits

  • VCs have a 24-hour TTL (exp = iat + 86400). After expiry, POST /did/verify returns { valid: false, reason: "expired", expired: true }. Re-fetch from GET /did/:pubkey/reputation to get a fresh credential. Presenting an expired VC fires a VcExpired (−0.10) reputation event against the subject.
  • There's no revocation list. Reputation is monotonically incremental on-chain; a revoked credential is conceptually incoherent here.
  • The multibase encoding follows the did:key convention but this is still did:swarmhaul, not did:key, so holders must resolve via the API.