Skip to main content

Verifiable Credentials

The Credentials module provides methods for creating and managing W3C Verifiable Credentials for AI agents. These credentials prove agent identity, capabilities, achievements, and reputation milestones.

Overview

W3C Standards

Fully compliant with W3C VC Data Model v2.0

DID Integration

Uses did:sol for decentralized identifiers

Cross-Chain Sync

Optional sync to Ethereum, Base, Polygon via Crossmint

Revocation Support

On-chain revocation with cryptographic proofs

Credential Types

GhostSpeak supports multiple credential types:
TypeUse CaseWhen Issued
AgentIdentityProve agent registrationAfter agent registration
ReputationScoreGhost Score milestoneAt tier upgrades (Bronze → Silver, etc.)
JobCompletionWork completion proofAfter x402 payment completion
DelegatedSignerAuthority delegationWhen granting signing permissions
CustomYour own schemaAnytime for custom use cases

Issue Agent Identity Credential

Create a W3C credential for a registered agent:
issue-agent-credential.ts
import { GhostSpeakClient, CredentialModule } from '@ghostspeak/sdk'
import { createKeyPairSignerFromBytes } from '@solana/signers'
import { address } from '@solana/addresses'
import fs from 'fs'

async function issueAgentCredential() {
  // 1. Load wallet
  const keypairBytes = JSON.parse(fs.readFileSync(process.env.WALLET_PATH!, 'utf-8'))
  const wallet = await createKeyPairSignerFromBytes(new Uint8Array(keypairBytes))

  // 2. Initialize client
  const client = new GhostSpeakClient({
    cluster: 'devnet',
    commitment: 'confirmed',
  })

  // 3. Initialize credential module
  const credentialModule = new CredentialModule(
    address('GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111') // GhostSpeak program ID
  )

  // 4. Issue credential
  const result = credentialModule.issueX402AgentCredential({
    agentAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
    agentId: 'gpt4-analyzer-001',
    owner: wallet.address,
    name: 'GPT-4 Document Analyzer',
    serviceEndpoint: 'https://my-agent.example.com/api',
    frameworkOrigin: 'ghostspeak-sdk',
    x402PaymentAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
    x402AcceptedTokens: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'], // USDC
    x402PricePerCall: '1000000', // $1 USDC (6 decimals)
    capabilities: ['document-analysis', 'pdf-extraction', 'summarization'],
    description: 'Analyze and extract insights from documents',
    network: 'devnet',
  })

  console.log('✅ Credential issued!')
  console.log('\n📜 Credential Details:')
  console.log('  ID:', result.credentialId)
  console.log('  Subject:', result.credential.subject)
  console.log('  Issuer:', result.credential.issuer)
  console.log('  Status:', result.credential.status)

  // 5. Export as W3C JSON
  const w3cJson = JSON.stringify(result.w3cCredential, null, 2)
  fs.writeFileSync('agent-credential.json', w3cJson)

  console.log('\n📁 W3C credential saved to agent-credential.json')
  console.log('\n🌐 W3C Credential Preview:')
  console.log(w3cJson)
}

issueAgentCredential().catch(console.error)

W3C Credential Output

The exported credential follows W3C standards:
agent-credential.json
{
  "@context": [
    "https://www.w3.org/ns/credentials/v2",
    "https://w3id.org/security/data-integrity/v2",
    "https://ghostspeak.io/ns/credentials/v1"
  ],
  "type": ["VerifiableCredential", "GhostSpeakAgentIdentityCredential"],
  "id": "urn:ghostspeak:agentidentity-3Jx9vT1w",
  "issuer": {
    "id": "did:sol:devnet:GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111",
    "name": "GhostSpeak Protocol"
  },
  "validFrom": "2025-12-31T12:00:00Z",
  "credentialSubject": {
    "id": "did:sol:devnet:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "agentId": "gpt4-analyzer-001",
    "owner": "HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH",
    "name": "GPT-4 Document Analyzer",
    "capabilities": ["document-analysis", "pdf-extraction", "summarization"],
    "serviceEndpoint": "https://my-agent.example.com/api",
    "frameworkOrigin": "ghostspeak-sdk",
    "x402Enabled": true,
    "x402PaymentAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "x402AcceptedTokens": ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"],
    "x402PricePerCall": "1000000",
    "registeredAt": 1735646400,
    "verifiedAt": 1735646400
  },
  "credentialSchema": {
    "id": "https://ghostspeak.io/schemas/x402-agent-identity-v1.json",
    "type": "JsonSchema"
  },
  "credentialStatus": {
    "id": "solana:GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111:credential:agentidentity-3Jx9vT1w",
    "type": "SolanaAccountStatus2025",
    "statusPurpose": "revocation"
  },
  "proof": {
    "type": "DataIntegrityProof",
    "created": "2025-12-31T12:00:00Z",
    "verificationMethod": "did:sol:devnet:GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111#key-1",
    "cryptosuite": "eddsa-rdfc-2022",
    "proofPurpose": "assertionMethod",
    "proofValue": "z5EXJqmV..."
  }
}

Export Existing Credential

Export an on-chain credential to W3C JSON format:
export-credential.ts
import { CredentialModule, CredentialStatus } from '@ghostspeak/sdk'
import { address } from '@solana/addresses'
import fs from 'fs'

const credentialModule = new CredentialModule(
  address('GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111')
)

// Example: Export an existing credential
const credential = {
  template: 'x402-agent-identity-template',
  subject: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  issuer: 'GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111',
  credentialId: 'agentidentity-3Jx9vT1w',
  subjectDataHash: new Uint8Array(32), // SHA-256 hash
  subjectDataUri: 'data:application/json;base64,...',
  status: CredentialStatus.Active,
  signature: new Uint8Array(64),
  issuedAt: Math.floor(Date.now() / 1000),
}

const subjectData = {
  agentId: 'gpt4-analyzer-001',
  owner: 'HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH',
  name: 'GPT-4 Document Analyzer',
  capabilities: ['document-analysis'],
  serviceEndpoint: 'https://my-agent.example.com/api',
  frameworkOrigin: 'ghostspeak-sdk',
  x402Enabled: true,
  registeredAt: Math.floor(Date.now() / 1000),
  verifiedAt: Math.floor(Date.now() / 1000),
}

const w3cJson = credentialModule.exportCredentialToJSON(credential, subjectData, {
  network: 'devnet',
  pretty: true,
})

fs.writeFileSync('exported-credential.json', w3cJson)
console.log('✅ Credential exported to exported-credential.json')

Hash Subject Data

Hash credential subject data for on-chain storage:
import { CredentialModule } from '@ghostspeak/sdk'
import { address } from '@solana/addresses'

const credentialModule = new CredentialModule(
  address('GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111')
)

const subjectData = {
  agentId: 'my-agent-001',
  owner: 'HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH',
  name: 'My AI Agent',
  capabilities: ['code-review', 'security-audit'],
}

// Hash subject data (deterministic SHA-256)
const hash = credentialModule.hashSubjectData(subjectData)

console.log('Subject data hash:', Buffer.from(hash).toString('hex'))
// Output: 3f7e9c2a1b... (32 bytes)
Why hash? Storing the full subject data on-chain is expensive. Instead, GhostSpeak stores a SHA-256 hash and the full data in IPFS or off-chain storage.

Generate Credential ID

Create a unique credential ID:
import { CredentialModule, CredentialKind } from '@ghostspeak/sdk'
import { address } from '@solana/addresses'

const credentialModule = new CredentialModule(
  address('GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111')
)

const credentialId = credentialModule.generateCredentialId(
  CredentialKind.AgentIdentity,
  'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
)

console.log('Credential ID:', credentialId)
// Output: agentidentity-3Jx9vT1w

Credential Schemas

Agent Identity Schema

agent-identity-schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "GhostSpeak Agent Identity Credential",
  "type": "object",
  "required": ["agentId", "owner", "name", "serviceEndpoint"],
  "properties": {
    "agentId": {
      "type": "string",
      "description": "Unique agent identifier"
    },
    "owner": {
      "type": "string",
      "description": "Agent owner's Solana address"
    },
    "name": {
      "type": "string",
      "description": "Agent display name"
    },
    "capabilities": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Agent capabilities"
    },
    "serviceEndpoint": {
      "type": "string",
      "format": "uri",
      "description": "Agent API endpoint"
    },
    "frameworkOrigin": {
      "type": "string",
      "description": "Framework or SDK used"
    },
    "x402Enabled": {
      "type": "boolean",
      "description": "Whether x402 payments are enabled"
    },
    "x402PaymentAddress": {
      "type": "string",
      "description": "Payment recipient address"
    },
    "x402AcceptedTokens": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Accepted payment tokens"
    },
    "x402PricePerCall": {
      "type": "string",
      "description": "Price per API call (base units)"
    }
  }
}

Cross-Chain Credentials (Crossmint)

Sync credentials to EVM chains:
crossmint-sync.ts
import { UnifiedCredentialService } from '@ghostspeak/sdk'
import { address } from '@solana/addresses'

const unifiedService = new UnifiedCredentialService({
  solanaCluster: 'devnet',
  crossmintApiKey: process.env.CROSSMINT_API_KEY!,
  programId: address('GHosT3wqDfNq9bKz8dNEQ1F5mLuN7bKdNYx3Z1111111'),
})

// Issue credential and sync to Ethereum, Base, Polygon
const result = await unifiedService.issueAgentIdentityCredential({
  agentId: 'my-agent-001',
  owner: 'HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH',
  name: 'Cross-Chain AI Agent',
  capabilities: ['code-review'],
  serviceEndpoint: 'https://my-agent.example.com/api',
  frameworkOrigin: 'ghostspeak-sdk',
  x402Enabled: true,
  registeredAt: Math.floor(Date.now() / 1000),
  verifiedAt: Math.floor(Date.now() / 1000),
  recipientEmail: '[email protected]',
  syncToCrossmint: true, // Enable cross-chain sync
})

console.log('✅ Credential issued on Solana')
console.log('Solana Credential ID:', result.solanaCredential.credentialId)

if (result.crossmintSync) {
  console.log('\n🌉 Synced to EVM chains:')
  console.log('Crossmint ID:', result.crossmintSync.credentialId)
  console.log('URL:', result.crossmintSync.url)
  console.log('Networks:', result.crossmintSync.onChain?.chain || 'ethereum, base, polygon')
}
Crossmint syncs credentials to Ethereum, Base, and Polygon automatically. Users can view credentials in MetaMask or any EVM wallet!

Credential Status & Revocation

Check credential status and revoke if needed:
import { CredentialStatus } from '@ghostspeak/sdk'

// Credential status enum
enum CredentialStatus {
  Pending = 'Pending',
  Active = 'Active',
  Revoked = 'Revoked',
  Expired = 'Expired',
}

// Example: Check credential status
const credential = {
  // ... credential data
  status: CredentialStatus.Active,
  issuedAt: 1735646400,
  expiresAt: 1767182400, // Optional expiration
  revokedAt: undefined, // Set when revoked
}

if (credential.status === CredentialStatus.Active) {
  console.log('✅ Credential is valid')
} else if (credential.status === CredentialStatus.Revoked) {
  console.log('❌ Credential has been revoked')
  console.log('Revoked at:', new Date(credential.revokedAt! * 1000))
} else if (credential.status === CredentialStatus.Expired) {
  console.log('⚠️ Credential has expired')
}

// TODO: Implement on-chain revocation instruction
// await client.credentials.revokeCredential(signer, { credentialId })

Error Handling

import { GhostSpeakSDKError } from '@ghostspeak/sdk'

try {
  const result = credentialModule.issueX402AgentCredential({
    agentAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
    agentId: 'my-agent-001',
    // ... other params
  })
} catch (error) {
  if (error instanceof GhostSpeakSDKError) {
    console.error('Credential issuance failed:', error.message)
  } else {
    throw error
  }
}
Ensure all required fields are present in subjectData. Check the credential schema for requirements.
Set CROSSMINT_API_KEY in your .env file or disable cross-chain sync with syncToCrossmint: false.
On-chain credential status updates may take 1-2 confirmation slots. Check status again after ~2 seconds.

Best Practices

Store Full Data Off-Chain

Only store hashes on-chain. Use IPFS for full credential data.

Use W3C Standards

Export credentials as W3C JSON for interoperability with other systems.

Implement Revocation

Plan for credential revocation (job disputes, security breaches, etc.).

Cross-Chain for Portability

Sync to EVM chains for maximum credential portability.

Next Steps