Getting Started
Integrate Solidus identity verification into your application in 5 minutes. By the end of this guide you will have created a DID, issued a verifiable credential, and verified it — all on the live testnet.
Prerequisites
- Node.js 18+
- A package manager (npm, yarn, or pnpm)
Install the SDK
npm install @solidus-network/sdk @solidus-network/auth @noble/ed25519@solidus-network/sdk provides DID operations, credential issuance, and chain queries. @solidus-network/auth handles authentication challenges and presentation verification. @noble/ed25519 generates the Ed25519 key pairs that Solidus uses for signing.
1. Generate a Key Pair
Every identity on Solidus starts with an Ed25519 key pair. The public key becomes the basis of your DID address; the private key signs transactions and credentials.
import * as ed25519 from '@noble/ed25519'
// Generate a new private key (32 random bytes)
const privateKey = ed25519.utils.randomPrivateKey()
const publicKey = await ed25519.getPublicKeyAsync(privateKey)
// Hex-encode for SDK configuration
const privateKeyHex = Buffer.from(privateKey).toString('hex')
const publicKeyHex = Buffer.from(publicKey).toString('hex')
console.log('Private key:', privateKeyHex)
console.log('Public key:', publicKeyHex)Keep your private key secret. Anyone with access to it controls the associated DID.
2. Create an SDK Instance
Connect to the Solidus testnet by creating an SDK instance with your key pair.
import { createSdk } from '@solidus-network/sdk'
const sdk = createSdk({
mode: 'testnet',
rpcUrl: 'https://rpc.solidus.network',
signerPrivateKey: privateKeyHex,
})The SDK instance is your entry point for all protocol operations — DIDs, credentials, and chain queries.
3. Create a DID
A Decentralized Identifier (DID) is your on-chain identity. Creating one submits a transaction to the Solidus testnet. did.create() registers the public key owned by the SDK’s configured signerPrivateKey — pass that same key’s public key.
const did = await sdk.did.create(publicKeyHex)
console.log('DID created:', did.id)
// Output: did:solidus:testnet:7Hk3mRtQZv...The returned DID follows the W3C DID standard with the format did:solidus:<network>:<address>, where the address is derived from a BLAKE3 hash of your public key.
4. Resolve a DID
You can look up any DID to retrieve its DID Document, which contains the public keys and verification methods associated with that identity.
const document = await sdk.did.resolve(did.id)
console.log('DID Document:', JSON.stringify(document, null, 2))The document includes verification methods, authentication keys, and assertion methods that other parties use to verify signatures from this DID.
5. Issue a Credential
Verifiable Credentials are cryptographic proofs of claims. Here we issue a simple email credential — in production, credentials like KYC levels are issued after real verification.
const credential = await sdk.credentials.issue({
subjectDid: did.id,
issuerDid: did.id,
issuerPrivateKey: privateKeyHex,
type: ['VerifiableCredential', 'EmailCredential'],
claims: {
email: '[email protected]',
verified: true,
},
expiresInDays: 365,
})
console.log('Credential issued:', credential.id)
console.log('Proof type:', credential.proof.type)The credential is signed with your private key and its hash is anchored on-chain, creating an immutable record that the credential was issued without storing any personal data on the blockchain.
6. Verify a Credential
Any party can verify a credential by its ID — the SDK looks it up on-chain and checks its signature, expiry, and revocation status.
const result = await sdk.credentials.verify(credential.id)
console.log('Valid:', result.valid)
console.log('Checks:', result.checks)
// Output: Valid: true
// Checks: { signature: true, expiry: true, revocation: true }Verification is entirely trustless — the verifier does not need to contact the issuer. They only need the credential ID and access to the Solidus network.
7. Authentication Flow
@solidus-network/auth lets you authenticate users by asking them to prove ownership of their DID. The flow has three steps: create a challenge, sign a presentation, and verify the presentation. createChallenge and verifyPresentation are plain functions, not client/verifier classes — the “verifier side” and “user side” below are just where each function call happens to run (your backend vs. the user’s device).
import { createChallenge, verifyPresentation } from '@solidus-network/auth'
// --- Verifier side: create a challenge for the user's DID ---
const challenge = createChallenge(did.id)
// challenge = { id, did, nonce, issuedAt, expiresAt }
// Send challenge.id and challenge.nonce to the user...
// --- User side: build and sign a Verifiable Presentation ---
// (Signing is application-specific — construct the presentation object per
// the VerifiablePresentation shape and sign challenge.nonce with the user's
// private key; see the @solidus-network/auth reference for the exact format.)
const presentation = {
'@context': ['https://www.w3.org/2018/credentials/v1'],
type: ['VerifiablePresentation'],
holder: did.id,
proof: {
type: 'Ed25519Signature2020',
created: new Date().toISOString(),
challenge: challenge.nonce,
proofPurpose: 'authentication',
verificationMethod: `${did.id}#key-0`,
jws: '...', // detached JWS over the presentation, signed with the user's private key
},
}
// --- Verifier side: verify the presentation ---
const verification = await verifyPresentation(
challenge,
presentation,
async (verificationMethodId) => {
// Resolve the DID to get its public key
const doc = await sdk.did.resolve(did.id)
// ...decode the matching verificationMethod's publicKeyMultibase to raw bytes
return publicKeyBytes
},
)
console.log('Authenticated:', verification.valid)
console.log('Checks:', verification.checks)The verifier never sees the user’s private key. They only receive a signed presentation that proves the user controls the DID.
Full Example
Here is the complete flow in a single script.
import * as ed25519 from '@noble/ed25519'
import { createSdk } from '@solidus-network/sdk'
import { createChallenge } from '@solidus-network/auth'
async function main() {
// Key generation
const privateKey = ed25519.utils.randomPrivateKey()
const publicKey = await ed25519.getPublicKeyAsync(privateKey)
const privateKeyHex = Buffer.from(privateKey).toString('hex')
const publicKeyHex = Buffer.from(publicKey).toString('hex')
// SDK setup
const sdk = createSdk({
mode: 'testnet',
rpcUrl: 'https://rpc.solidus.network',
signerPrivateKey: privateKeyHex,
})
// Create DID
const did = await sdk.did.create(publicKeyHex)
console.log('DID:', did.id)
// Issue credential
const credential = await sdk.credentials.issue({
subjectDid: did.id,
issuerDid: did.id,
issuerPrivateKey: privateKeyHex,
type: ['VerifiableCredential', 'EmailCredential'],
claims: {
email: '[email protected]',
verified: true,
},
expiresInDays: 365,
})
console.log('Credential:', credential.id)
// Verify credential (by ID)
const result = await sdk.credentials.verify(credential.id)
console.log('Valid:', result.valid)
// Authentication — create a challenge for the DID to answer
// (see the @solidus-network/auth reference for signing + verifyPresentation)
const challenge = createChallenge(did.id)
console.log('Challenge nonce:', challenge.nonce)
}
main().catch(console.error)Next Steps
- How Solidus Works — understand the protocol design
- DIDs in depth — DID document structure, key derivation, lifecycle
- Verifiable Credentials — credential types, selective disclosure, revocation
- Network and Testnet — RPC methods, explorer, faucet