Skip to main content
Home
This release is 1 version behind 0.4.6 โ€” the latest version of @sadie-project/deno-nitro-instance. Jump to latest
It is unknown whether this package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers
It is unknown whether this package works with Cloudflare Workers
It is unknown whether this package works with Node.js
It is unknown whether this package works with Deno
It is unknown whether this package works with Bun
It is unknown whether this package works with Browsers
JSR Score
52%
Published
4 days ago (0.4.5)

๐Ÿ”Œ SADIE Instance Package

Client Library VSOCK CBOR

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 using declarations
  • 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 inputs
  • close(): Close the connection and cleanup resources
  • [Symbol.asyncDispose](): Automatic cleanup with await 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 serialization
  • node-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(),
  };
};


Part of the SADIE Deno Nitro Framework

๐Ÿ  Back to Main README โ€ข ๐Ÿ“– Full Documentation

New Ticket: Report package

Please provide a reason for reporting this package. We will review your report and take appropriate action.

Please review the JSR usage policy before submitting a report.

Add Package

deno add jsr:@sadie-project/deno-nitro-instance

Import symbol

import * as deno_nitro_instance from "@sadie-project/deno-nitro-instance";
or

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
or (using pnpm 10.8 or older)
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
or (using Yarn 4.8 or older)
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";