4 min read

ProvnZero SDK (TypeScript)#

npm version TypeScript

The TypeScript/Node.js SDK for the ProvnZero proxy. It handles HPKE sealing, receipt verification, structured sync responses, and verified streaming against the proxy's attestation key.

Features#

  • Standard HPKE: Implements HPKE Base mode via @noble.
  • Async API: Clean Promise-based interface.
  • Small Dependency Surface: Lightweight crypto-focused footprint.
  • Client-Side Sealing: Manages ephemeral keypairs and session isolation automatically.
  • Verified Receipts: Validates signed sync receipts before returning them to the caller.
  • Structured Responses: Preserves assistant messages, finish reasons, and tool calls through the sealed sync path.
  • Verified Streaming: Decrypts chunk envelopes and checks the signed terminal proof before accepting stream completion.

Installation#

Bash
npm install provnzero-sdk

Basic Usage#

TYPESCRIPT
import { ProvnZeroClient } from 'provnzero-sdk';

const client = new ProvnZeroClient('http://localhost:3001');

async function askSecurely() {
  try {
    await client.init();

    const response = await client.send({
      prompt: "What is the capital of France?",
      provider: "openai"
    });

    console.log("AI Response:", response.text);
    console.log("Verified:", response.receiptVerified);
    console.log("Verified Receipt:", response.verifiedReceipt);
  } catch (err) {
    console.error("Secure call failed:", err);
  }
}

askSecurely();

client.send("What is the capital of France?") is also supported for the common case.

The object form also supports system, messages, tools, tool_choice, temperature, and max_tokens when you need to preserve multi-turn chat context or tool calls through the proxy.

TYPESCRIPT
const toolResponse = await client.send({
  messages: [{ role: "user", content: "Look up Brussels weather." }],
  tools: [
    {
      type: "function",
      function: {
        name: "get_weather",
        description: "Get the weather for a city",
        parameters: {
          type: "object",
          properties: {
            location: { type: "string" }
          },
          required: ["location"]
        }
      }
    }
  ],
  tool_choice: "auto"
});

console.log(toolResponse.message?.tool_calls?.[0]?.function?.name);

Streaming#

TYPESCRIPT
const client = new ProvnZeroClient("http://localhost:3001");
await client.init();

let text = "";
for await (const chunk of client.stream("Stream a short confirmation.")) {
  text += chunk.text;
}

const proof = client.getLastStreamProof();
console.log(text, proof?.final_root);

Security#

The SDK performs client-side sealing before the prompt ever leaves your memory.

  1. It requests the server's public key.
  2. It generates a fresh ephemeral keypair.
  3. It performs HPKE setup_sender to derive a secret key.
  4. It seals the prompt using AES-256-GCM.
  5. Only the ciphertext and encapsulated key are sent to the proxy.

For sync responses, the SDK verifies the signed receipt against the proxy's attestation key before returning verifiedReceipt.

For streaming, the SDK:

  1. Seals the request as usual.
  2. Decrypts each returned chunk envelope client-side.
  3. Recomputes the rolling chunk hash-chain locally using the same vex-core-aligned hashing rules as the proxy.
  4. Verifies the signed terminal proof before treating the stream as complete.

If you already trust the proxy's attestation key out of band, pass it at construction time:

TYPESCRIPT
const client = new ProvnZeroClient("http://localhost:3001", {
  attestationPublicKey: process.env.PROVNZERO_ATTESTATION_KEY,
});

For planned key rotation windows, pass more than one trusted key:

TYPESCRIPT
const client = new ProvnZeroClient("http://localhost:3001", {
  attestationPublicKeys: [
    process.env.PROVNZERO_ATTESTATION_KEY_CURRENT!,
    process.env.PROVNZERO_ATTESTATION_KEY_NEXT!,
  ],
});

If you do not pin a key, the SDK trusts the first X-Provn-Attestation-Key it sees for the lifetime of the client instance and rejects later key changes. That is safer than blindly following each response header, but explicit key pinning or a short allow-list is still the stronger production posture.

The receipt and final-proof payloads the SDK verifies are currently ProvnZero-local v1 contracts mirrored from the proxy. They are intentionally kept stable until VEX publishes canonical shared schemas.

Development#

Bash
# Install dependencies
npm install

# Build the SDK
npm run build

# Run tests
npm test

License#

Apache 2.0

Found something unclear or incorrect?Report issueor useEdit this page
Edit this page on GitHub