๐ SADIE Instance Package
Enclave Connection Client
TypeScript client library for connecting to and executing contracts in SADIE
enclaves
๐ฏ Purpose
The SADIE Instance Package provides a high-level TypeScript client library for communicating with SADIE enclaves. It handles the complexity of VSOCK communication, CBOR serialization, and contract execution, making it easy to integrate secure enclave computing into your applications.
๐ Key Features
- Type-Safe Contracts: Full TypeScript support for contract definitions
- Multiple Connection Types: Support for both development (Deno) and production (Nitro) enclaves
- Automatic Serialization: Seamless CBOR encoding/decoding of messages
- Resource Management: Automatic cleanup with
usingdeclarations - Error Handling: Comprehensive error handling and connection management
๐๏ธ Architecture
classDiagram class EnclaveConnection { <<abstract>> +execute(contract, inputs) +close() -encoder: Encoder -decoder: DecoderStream -writer: WritableStreamDefaultWriter } class DenoEnclave { +constructor() +execute(contract, inputs) +close() } class NitroEnclave { +constructor() +execute(contract, inputs) +close() } class EnclaveCommand { -enclave: ChildProcess +constructor(command, args) +close() } EnclaveConnection <|-- EnclaveCommand EnclaveCommand <|-- DenoEnclave EnclaveCommand <|-- NitroEnclave
๐ฆ Package Structure
packages/instance/ โโโ mod.ts # Main module exports โโโ mod_test.ts # Unit tests โโโ contract.ts # Example contract โโโ inputs.json # Example inputs โโโ test-quote.b64 # Sample quote for testing โโโ deno.json # Package configuration โโโ README.md # This file
๐ Quick Start
Installation
// Import the client library import { DenoEnclave, NitroEnclave } from "@sadie-project/deno-nitro-instance";
Basic Usage
import { DenoEnclave } from "@sadie-project/deno-nitro-instance"; // Define a contract function const myContract = (inputs: { message: string; count: number }) => { return { result: inputs.message.repeat(inputs.count), timestamp: new Date().toISOString(), }; }; // Execute in enclave await using enclave = new DenoEnclave(); const response = await enclave.execute(myContract, { message: "Hello ", count: 3, }); console.log(response.output); // { result: "Hello Hello Hello ", timestamp: "..." } console.log(response.quoteB64); // Base64 encoded attestation quote console.log(response.contract); // Contract data URL console.log(response.inputs); // Original inputs
๐ ๏ธ API Reference
Contract Types
The instance package supports flexible contract definitions:
export type Contract<I = unknown, O = unknown> = | string | Uint8Array | ((inputs: I) => O | Promise<O>);
EnclaveConnection (Abstract Base)
The base class for all enclave connections:
export abstract class EnclaveConnection { #decoder = new DecoderStream(); #encoder = new Encoder(); #writer: WritableStreamDefaultWriter<Uint8Array>; constructor(input: WritableStreamDefaultWriter, output: ReadableStream) { this.#writer = input; (async () => { for await (const chunk of output) { this.#decoder.write(chunk); } })(); } async [Symbol.asyncDispose]() { await this.close(); } }
Methods
execute<I, O>(contract: Contract<I, O>, inputs: I): Execute a contract with inputsclose(): Close the connection and cleanup resources[Symbol.asyncDispose](): Automatic cleanup withawait using
DenoEnclave
For development and testing, runs contracts in a local Deno subprocess:
/** * Run the enclave code in a child deno instance instead of a real enclave * * WARN: This means no enclave attestation is possible */ export class DenoEnclave extends EnclaveCommand { constructor() { super(Deno.execPath(), [ "run", "--allow-ffi", "--allow-env", "@sadie-project/deno-nitro-enclave", ]); } }
Features:
- โ Full TypeScript contract support
- โ Fast development iteration
- โ Complete API compatibility
- โ ๏ธ No real attestation (mock quotes only)
NitroEnclave
For production use with real AWS Nitro Enclaves:
export class NitroEnclave extends EnclaveCommand { constructor() { super("nitro-cli", ["run-enclave"]); } }
Features:
- โ Hardware-based attestation
- โ Cryptographic security guarantees
- โ Production-ready isolation
- โ ๏ธ Requires AWS Nitro-enabled instances
๐ Contract Examples
Simple Computation
const mathContract = (inputs: { a: number; b: number; operation: string }) => { switch (inputs.operation) { case "add": return { result: inputs.a + inputs.b }; case "multiply": return { result: inputs.a * inputs.b }; case "power": return { result: Math.pow(inputs.a, inputs.b) }; default: throw new Error(`Unknown operation: ${inputs.operation}`); } }; await using enclave = new DenoEnclave(); const result = await enclave.execute(mathContract, { a: 10, b: 3, operation: "power", }); console.log(result.output); // { result: 1000 }
Data Processing
interface DataRecord { id: string; value: number; category: string; } const analyzeData = (inputs: { records: DataRecord[] }) => { const byCategory = inputs.records.reduce( (acc, record) => { if (!acc[record.category]) { acc[record.category] = []; } acc[record.category].push(record.value); return acc; }, {} as Record<string, number[]>, ); const analysis = Object.entries(byCategory).map(([category, values]) => ({ category, count: values.length, sum: values.reduce((a, b) => a + b, 0), average: values.reduce((a, b) => a + b, 0) / values.length, min: Math.min(...values), max: Math.max(...values), })); return { total_records: inputs.records.length, categories: analysis, processed_at: new Date().toISOString(), }; };
String-based Contracts
You can also provide contracts as strings:
const contractCode = ` export default function(inputs) { return { reversed: inputs.text.split('').reverse().join(''), length: inputs.text.length, uppercase: inputs.text.toUpperCase(), }; }`; await using enclave = new DenoEnclave(); const result = await enclave.execute(contractCode, { text: "Hello World!" });
๐ง Configuration
Package Configuration
{ "name": "@sadie-project/deno-nitro-instance", "exports": "./mod.ts", "imports": { "@std/assert": "jsr:@std/assert@1", "cbor-x": "npm:cbor-x@^1.6.0", "node-vsock": "npm:node-vsock@^0.0.5" } }
Dependencies
cbor-x: Efficient CBOR serializationnode-vsock: VSOCK communication for Nitro Enclaves@std/assert: Standard assertions for testing
๐งช Testing & Development
Running Tests
# Run all instance tests deno test --allow-all packages/instance/ # Run specific test deno test --allow-all packages/instance/mod_test.ts # Run with watch mode deno test --allow-all --watch packages/instance/
Example Test Contract
The package includes an example contract for testing:
/** * Example contract function */ export default function testConstract(inputs: unknown) { return { foo: 1, ...inputs, }; }
Test Data
Example inputs are provided in inputs.json:
# Test with example contract and inputs deno run --allow-all packages/instance/mod.ts < packages/instance/inputs.json
๐ Integration Examples
With Express.js Server
import express from "npm:express"; import { DenoEnclave } from "@sadie-project/deno-nitro-instance"; const app = express(); app.use(express.json()); app.post("/execute", async (req, res) => { const { contract, inputs } = req.body; try { await using enclave = new DenoEnclave(); const result = await enclave.execute(contract, inputs); res.json({ success: true, output: result.output, quote: result.quoteB64, timestamp: new Date().toISOString(), }); } catch (error) { res.status(500).json({ success: false, error: error.message, }); } }); app.listen(3000);
With WebSocket
import { serve } from "https://deno.land/std/http/server.ts"; import { DenoEnclave } from "@sadie-project/deno-nitro-instance"; serve(async (req) => { if (req.headers.get("upgrade") === "websocket") { const { socket, response } = Deno.upgradeWebSocket(req); socket.onmessage = async (event) => { try { const { contract, inputs } = JSON.parse(event.data); await using enclave = new DenoEnclave(); const result = await enclave.execute(contract, inputs); socket.send( JSON.stringify({ type: "result", data: result, }), ); } catch (error) { socket.send( JSON.stringify({ type: "error", message: error.message, }), ); } }; return response; } return new Response("WebSocket server"); });
๐ Security Considerations
Contract Validation
Always validate contract code before execution:
function validateContract(contract: string): boolean { // Basic validation - extend as needed if (contract.includes("eval(") || contract.includes("Function(")) { return false; // Potentially dangerous } if (!contract.includes("export default")) { return false; // Must have default export } return true; } // Use in your application if (typeof contract === "string" && !validateContract(contract)) { throw new Error("Contract failed validation"); }
Input Sanitization
Sanitize inputs to prevent injection attacks:
function sanitizeInputs(inputs: unknown): unknown { // Remove potentially dangerous properties if (typeof inputs === "object" && inputs !== null) { const cleaned = { ...(inputs as object) }; delete (cleaned as any).__proto__; delete (cleaned as any).constructor; delete (cleaned as any).prototype; return cleaned; } return inputs; }
Quote Verification
In production, always verify attestation quotes:
import { validate } from "@qlever-llc/deno-nitro"; async function verifyExecution(result: any) { const isValid = await validate(result.quote); if (!isValid) { throw new Error("Attestation quote verification failed"); } return result; }
๐ Troubleshooting
Common Issues
Connection Timeouts
Error: Connection timeout
- Check if enclave is running and accessible
- Verify VSOCK configuration
- Ensure proper network setup for Nitro instances
Serialization Errors
Error: Cannot encode circular structure
- Remove circular references from inputs
- Ensure all data is JSON-serializable
- Check for functions or symbols in input data
Contract Execution Failures
Error: Contract execution failed
- Verify contract syntax and default export
- Check contract dependencies and imports
- Validate input data types and structure
Debug Mode
Enable debug logging:
// Set environment variable for debug output Deno.env.set("DEBUG", "sadie:*"); // Or enable verbose logging in code const enclave = new DenoEnclave(); // Add custom logging as needed
๐ Performance Tips
Connection Reuse
Reuse connections for better performance:
// Good: Reuse connection for multiple executions await using enclave = new DenoEnclave(); const results = await Promise.all([ enclave.execute(contract1, inputs1), enclave.execute(contract2, inputs2), enclave.execute(contract3, inputs3), ]); // Bad: Create new connection for each execution // This is slower and uses more resources
Batch Processing
Process multiple items in a single contract:
const batchContract = (inputs: { items: any[] }) => { return { results: inputs.items.map((item) => processItem(item)), total: inputs.items.length, batch_id: crypto.randomUUID(), }; };
๐ Related Packages
@sadie-project/deno-nitro-enclave- Secure execution environment for contracts@qlever-llc/deno-nitro- Low-level Nitro integration
Part of the SADIE Deno Nitro Framework
Add Package
deno add jsr:@sadie-project/deno-nitro-instance
Import symbol
import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";
Import directly with a jsr specifier
import * as deno_nitro_instance from "jsr:@sadie-project/deno-nitro-instance";
Add Package
pnpm i jsr:@sadie-project/deno-nitro-instance
pnpm dlx jsr add @sadie-project/deno-nitro-instance
Import symbol
import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";
Add Package
yarn add jsr:@sadie-project/deno-nitro-instance
yarn dlx jsr add @sadie-project/deno-nitro-instance
Import symbol
import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";
Add Package
vlt install jsr:@sadie-project/deno-nitro-instance
Import symbol
import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";
Add Package
npx jsr add @sadie-project/deno-nitro-instance
Import symbol
import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";
Add Package
bunx jsr add @sadie-project/deno-nitro-instance
Import symbol
import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";