Blockstream Enterprise Custody SDK

Setup and Installation

Canonical setup guide for the Blockstream Enterprise Custody JavaScript SDK, including registry access, package selection, key generation, and first-time SDK initialization.

This page is the canonical setup guide for the Blockstream Enterprise Custody JavaScript SDK. Complete it before following the onboarding, wallet, tokenization, or request guides.

Before you begin

You need:

  • Node.js 18 or later
  • Access to a running Blockstream Enterprise Custody instance
  • Access to the private JavaScript package registry
  • A valid npm auth token for that registry
  • An admin or existing onboarding flow that can provision your device credentials

How to set up the JS registry

The SDK packages are distributed through a private registry. Installing them requires both:

  • access to the private package repository or registry
  • an npm auth token

If either one is missing, package installation will fail.

Add this to your project-level .npmrc or user-level ~/.npmrc:

@blockstream:registry=https://enterprise.blockstream.com/integrations/npm
//enterprise.blockstream.com/integrations/npm/:_authToken=${NPM_TOKEN}

Then export the token:

export NPM_TOKEN=your_token_here

Ask your Blockstream administrator for:

  • the registry URL
  • the npm token
  • confirmation that your account can access the SDK packages

Choose the packages you need

PackageInstall when you need itWhy it exists
@blockstream/crypticAlwaysRuntime client for signing, encryption, decryption, and COSE
@blockstream/ecs-registryMost ECS integrationsTyped ECS request and response schemas
@blockstream/amp-registryAMP or tokenization flows on LiquidTyped AMP-specific request and response schemas
zodWhen using any registry packagePeer dependency used for schema parsing and typing
node-forgeWhen using @blockstream/crypticRuntime dependency externalized by the library build
uuidOptional, only if your application uses generated IDsConvenient for request IDs and resource creation examples
@noble/curvesOptional, only for manual ECDSA key generation examplesAdvanced/manual key generation path

@blockstream/registry-shared exists to support the registry packages and is not usually a package that application developers install directly.

Install the SDK

Core ECS integration

Use this for most applications that need authenticated ECS requests and typed ECS schemas.

npm install @blockstream/cryptic @blockstream/ecs-registry zod node-forge

AMP and tokenization

Add the AMP registry if you are issuing assets, managing restrictions, or working with AMP wallet flows on Liquid.

npm install @blockstream/cryptic @blockstream/ecs-registry @blockstream/amp-registry zod node-forge

Credentials and values you will need

The SDK constructor needs five runtime values:

ValueSourcePurpose
clientRsaPrivateKeyPemGenerated locallyDecrypt server responses
clientEcdsaPrivateKeyGenerated locallySign requests
deviceUuidReturned by the server during API-user creation or regular-user device authorizationBind requests to a registered device
serverRsaPublicKeyPemReturned during onboarding or provided by your Blockstream administratorEncrypt request material for the server
serverEcdsaPublicKeyReturned during onboarding or provided by your Blockstream administratorVerify server signatures

About deviceUuid

deviceUuid is generated by the server. You do not invent it locally.

It is returned by one of these flows:

  • API user onboarding: the server returns device_uuid when the user is created
  • Regular user onboarding: the server returns the permanent device_uuid when the invitation is authorized

Persist deviceUuid with the matching private keys. Future authenticated requests depend on that exact pairing.

Key generation

There are two supported approaches:

  • Recommended: use the SDK helper
  • Advanced: generate keys manually with @noble/curves plus Web Crypto or node-forge

Use the helper from @blockstream/cryptic/utils unless you have a specific reason to control key generation yourself.

import { generateUserKeyPairs } from '@blockstream/cryptic/utils'

const keys = await generateUserKeyPairs()

console.log(keys.ecdsa.privateKeyHex)
console.log(keys.ecdsa.publicKeyHex)
console.log(keys.rsa.privateKeyPem)
console.log(keys.rsa.publicKeyPem)

The helper returns:

  • keys.ecdsa.privateKeyHex
  • keys.ecdsa.publicKeyHex
  • keys.rsa.privateKeyPem
  • keys.rsa.publicKeyPem

When you construct Blockstream, convert the ECDSA hex key into bytes:

import { Buffer } from 'buffer'
import { Blockstream } from '@blockstream/cryptic'
import { generateUserKeyPairs } from '@blockstream/cryptic/utils'

const keys = await generateUserKeyPairs()

const client = new Blockstream(
  keys.rsa.privateKeyPem,
  Buffer.from(keys.ecdsa.privateKeyHex, 'hex'),
  deviceUuid,
  serverRsaPublicKeyPem,
  Buffer.from(serverEcdsaPublicKeyHex, 'hex'),
)

Advanced: manual ECDSA generation with @noble/curves

Install @noble/curves only if you are not using the built-in helper:

npm install @noble/curves
import { Buffer } from 'buffer'
import { p256 } from '@noble/curves/nist'

const ecdsaPrivateKey = p256.utils.randomSecretKey()
const ecdsaPublicKey = p256.getPublicKey(ecdsaPrivateKey, true)

const ecdsaPrivateKeyHex = Buffer.from(ecdsaPrivateKey).toString('hex')
const ecdsaPublicKeyHex = Buffer.from(ecdsaPublicKey).toString('hex')

Advanced: manual RSA generation with Web Crypto

async function exportPem(key: CryptoKey, format: 'pkcs8' | 'spki') {
  const exported = await crypto.subtle.exportKey(format, key)
  const base64 = btoa(String.fromCharCode(...new Uint8Array(exported)))
  const lines = base64.match(/.{1,64}/g) ?? []

  if (format === 'pkcs8') {
    return `-----BEGIN PRIVATE KEY-----\n${lines.join('\n')}\n-----END PRIVATE KEY-----`
  }

  return `-----BEGIN PUBLIC KEY-----\n${lines.join('\n')}\n-----END PUBLIC KEY-----`
}

const rsaKeyPair = await crypto.subtle.generateKey(
  {
    name: 'RSA-OAEP',
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: 'SHA-256',
  },
  true,
  ['encrypt', 'decrypt'],
)

const rsaPrivateKeyPem = await exportPem(rsaKeyPair.privateKey, 'pkcs8')
const rsaPublicKeyPem = await exportPem(rsaKeyPair.publicKey, 'spki')

Advanced: manual RSA generation with node-forge

Use this path if your environment already standardizes on node-forge for key material handling.

import forge from 'node-forge'

const rsaKeyPair = forge.pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 })

const rsaPrivateKeyPem = forge.pki.privateKeyToPem(rsaKeyPair.privateKey)
const rsaPublicKeyPem = forge.pki.publicKeyToPem(rsaKeyPair.publicKey)

Initialize the SDK

Once you have your key material, deviceUuid, and the server public keys, create a reusable Blockstream instance:

import { Buffer } from 'buffer'
import { Blockstream } from '@blockstream/cryptic'

const client = new Blockstream(
  clientRsaPrivateKeyPem,
  Buffer.from(clientEcdsaPrivateKeyHex, 'hex'),
  deviceUuid,
  serverRsaPublicKeyPem,
  Buffer.from(serverEcdsaPublicKeyHex, 'hex'),
)

Verify the setup

Make a simple authenticated request to confirm the client is wired correctly:

const request = await client.request({
  action: 'get',
  resource: '/users/me',
})

const response = await fetch(`${ECS_BASE_URL}/request`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/octet-stream' },
  body: Buffer.from(request),
})

const result = await client.parse(new Uint8Array(await response.arrayBuffer()))
console.log(result.payload)

If you receive your user profile back, the setup is working.

Where to go next

On this page