Beltic logo
Cli guideWorkflows

Creating Your First Credential

End-to-end tutorial creating a DeveloperCredential from template to verified JWS token.

This tutorial walks you through creating, signing, and verifying your first Beltic credential using the CLI.

What You'll Build

A complete DeveloperCredential workflow:

  1. Start from a template
  2. Customize for your identity
  3. Validate the structure
  4. Generate keys
  5. Sign into a JWS token
  6. Verify the signature

Time required: 15-20 minutes

Prerequisites

  • Beltic CLI installed (Installation guide)
  • beltic-spec repository cloned
  • AJV CLI for validation: npm install -g ajv-cli ajv-formats

Step 1: Choose a Template

Navigate to the beltic-spec examples:

cd beltic-spec/examples/developer/v1/tests
ls

Available templates:

  • valid-individual-minimal.json - Solo developer, basic fields
  • valid-individual-complete.json - Individual with full KYB
  • valid-organization-tier1.json - Startup, basic org info
  • valid-organization-tier2-complete.json - Established company

Choose based on your entity type and KYB tier needs.

Step 2: Copy and Customize

cp valid-individual-minimal.json ~/my-developer-credential.json
cd ~

Edit my-developer-credential.json with your information:

{
  "credentialId": "generate-new-uuid-here",
  "legalName": "Your Legal Name or Company Name",
  "entityType": "individual",  // or "corporation", "limited_liability_company"
  "incorporationJurisdiction": {
    "country": "US",
    "region": "CA"  // Optional: state/province
  },
  "businessEmail": "your.email@example.com",
  "kybTier": "tier_1_basic",
  "subjectDid": "did:web:yourdomain.com",
  "issuerDid": "did:web:issuer.beltic.dev",
  "issuanceDate": "2025-01-15T00:00:00Z",
  "expirationDate": "2026-01-15T00:00:00Z",
  ...
}

Generate UUID:

# On macOS/Linux
uuidgen | tr '[:upper:]' '[:lower:]'

# Or use online generator

Key fields to update:

  • credentialId - New UUID
  • legalName - Your name/company
  • entityType - Match your entity
  • businessEmail - Your email
  • subjectDid - Your DID
  • issuanceDate / expirationDate - Current dates

Step 3: Validate Against Schema

ajv validate \
  -s beltic-spec/schemas/developer/v1/developer-credential-v1.schema.json \
  -d my-developer-credential.json

Success output:

my-developer-credential.json valid

Error output:

my-developer-credential.json invalid
[
  {
    "instancePath": "/businessEmail",
    "message": "must match format \"email\""
  }
]

Fix any errors before proceeding.

Step 4: Generate Cryptographic Keys

beltic keygen \
  --algorithm EdDSA \
  --output dev-private-key.pem

Output:

Generated EdDSA keypair
Private key: dev-private-key.pem
Public key: dev-private-key.pub.pem

Important: Keep dev-private-key.pem secure! Never commit to version control.

Add to .gitignore:

echo "*.pem" >> .gitignore
echo "*.jwt" >> .gitignore

Step 5: Sign the Credential

beltic sign \
  --payload my-developer-credential.json \
  --key dev-private-key.pem \
  --algorithm EdDSA \
  --output developer-credential.jwt \
  --kid did:web:yourdomain.com#dev-key-1

Output:

Validating credential against schema...
Schema validation: ✓ PASSED
Signing credential...
Signed successfully
Token written to: developer-credential.jwt
Token size: 2,847 bytes

The JWT token contains:

  • Header: Algorithm, type (application/beltic-developer+jwt), key ID
  • Payload: Issuer, subject, expiration, full credential
  • Signature: EdDSA signature over header + payload

Step 6: Verify the Signature

beltic verify \
  --token developer-credential.jwt \
  --key dev-private-key.pub.pem

Output:

Verifying JWS token...
✓ Signature valid
✓ Claims valid
✓ Schema valid

Credential Details:
  ID: your-credential-id
  Subject: did:web:yourdomain.com
  Issuer: did:web:issuer.beltic.dev
  Issued: 2025-01-15T00:00:00Z
  Expires: 2026-01-15T00:00:00Z
  Type: DeveloperCredential v1
  Legal Name: Your Name
  Entity Type: individual
  KYB Tier: tier_1_basic

VALID

Step 7: Inspect the JWT

To see the JWT structure:

# Extract payload (base64 decode)
cat developer-credential.jwt | cut -d'.' -f2 | base64 -d | jq .

Output:

{
  "iss": "did:web:issuer.beltic.dev",
  "sub": "did:web:yourdomain.com",
  "jti": "your-credential-id",
  "iat": 1699876800,
  "nbf": 1699876800,
  "exp": 1731412800,
  "vc": {
    "credentialId": "your-credential-id",
    "legalName": "Your Name",
    ...
  }
}

Advanced: Constrained Verification

Verify with specific issuer/audience expectations:

beltic verify \
  --token developer-credential.jwt \
  --key dev-private-key.pub.pem \
  --issuer did:web:issuer.beltic.dev \
  --audience did:web:platform.example.com

This ensures the credential was issued by the expected issuer and intended for a specific audience.

SDK Workflow (TypeScript)

The TypeScript SDK provides programmatic credential workflows. Note: SDK is currently in early access.

Step 1: Install SDK

npm install @beltic/sdk

Step 2: Validate Credential

import { validateDeveloperCredential, ValidationError } from '@beltic/sdk';

const credential = {
  credentialId: 'your-uuid',
  legalName: 'Your Name',
  // ... rest of credential
};

const result = validateDeveloperCredential(credential);
if (!result.ok) {
  const error = new ValidationError(result.errors);
  console.error(error.format());
  process.exit(1);
}

console.log('Credential valid:', result.value);

Step 3: Generate Keys

import { generateKeyPair } from '@beltic/sdk';

const { privateKey, publicKey } = await generateKeyPair('EdDSA');
console.log('Keys generated');

Step 4: Sign Credential

import { signCredential } from '@beltic/sdk';

const token = await signCredential(credential, privateKey, {
  alg: 'EdDSA',
  issuerDid: 'did:web:issuer.beltic.dev',
  subjectDid: credential.subjectDid,
  keyId: 'did:web:issuer.beltic.dev#key-1',
});

console.log('Signed token:', token);

Step 5: Verify Credential

import { verifyCredential } from '@beltic/sdk';

const verified = await verifyCredential(token, {
  keyResolver: async ({ kid, alg, iss }) => {
    // Resolve public key from DID or key store
    return publicKey;
  },
  expectedIssuer: 'did:web:issuer.beltic.dev',
});

console.log('Verified credential:', verified.credential);

Self-Signing with SDK

// Self-sign where issuer = subject
const myDid = 'did:web:developer.example.com';
const selfSignedToken = await signCredential(credential, privateKey, {
  alg: 'EdDSA',
  issuerDid: myDid,
  subjectDid: myDid,  // Same as issuer
  keyId: `${myDid}#key-1`,
});

Complete SDK Example

import {
  validateDeveloperCredential,
  signCredential,
  verifyCredential,
  generateKeyPair,
  ValidationError,
} from '@beltic/sdk';

async function createCredential() {
  // 1. Validate
  const credential = { /* your credential */ };
  const validation = validateDeveloperCredential(credential);
  if (!validation.ok) {
    throw new ValidationError(validation.errors);
  }

  // 2. Generate keys
  const { privateKey, publicKey } = await generateKeyPair('EdDSA');

  // 3. Sign
  const token = await signCredential(credential, privateKey, {
    alg: 'EdDSA',
    issuerDid: 'did:web:issuer.beltic.dev',
    subjectDid: credential.subjectDid,
    keyId: 'did:web:issuer.beltic.dev#key-1',
  });

  // 4. Verify
  const verified = await verifyCredential(token, {
    keyResolver: async () => publicKey,
  });

  return { token, verified };
}

Complete Example Script

#!/bin/bash
set -e

# 1. Copy template
cp beltic-spec/examples/developer/v1/tests/valid-individual-minimal.json my-dev-cred.json

# 2. Edit file (manual step - update fields)
echo "Update my-dev-cred.json with your information, then press Enter"
read

# 3. Validate
ajv validate -s beltic-spec/schemas/developer/v1/developer-credential-v1.schema.json -d my-dev-cred.json

# 4. Generate keys
beltic keygen --algorithm EdDSA --output dev-key.pem

# 5. Sign
beltic sign \
  --payload my-dev-cred.json \
  --key dev-key.pem \
  --algorithm EdDSA \
  --output developer.jwt \
  --kid did:web:example.com#key-1

# 6. Verify
beltic verify --token developer.jwt --key dev-key.pub.pem

echo "✓ Developer credential created successfully!"

Next Steps