Skip to content

MakePrisms/OpenSecret-SDK

 
 

Repository files navigation

OpenSecret SDK

This is a JavaScript/TypeScript SDK for the OpenSecret platform. It is framework-agnostic with no React (or other UI-framework) dependency, and runs in the browser, React Native, Node, and Bun.

🚧 We're currently in preview mode, please contact us at team@opensecret.cloud for the preview URL and getting started info 🚧

Installation

npm install @agicash/opensecret

Usage

Configure the SDK once at startup, then call the API functions directly. The SDK is framework-agnostic — use it from any UI layer (React, Vue, Svelte, vanilla JS) or a non-UI runtime (Node, Bun, CLI, MCP).

import { configure, browserStorage, signIn, signUp, signOut, get, put, list, del } from "@agicash/opensecret";

// Configure once at app initialization
configure({
  apiUrl: "{URL}",
  clientId: "{PROJECT_UUID}",
  storage: browserStorage
});

// Then call the functions directly
await signIn("email", "password"); // tokens are persisted through your StorageProvider
await signUp("email", "password", "inviteCode");
await put("key", "value");
const value = await get("key");
const items = await list();
await del("key");
await signOut();

Storage

The SDK persists auth tokens and the encrypted-session handshake through a StorageProvider you pass to configure({ storage }). Storage is required.

Browser apps can use the bundled browserStorage helper, which maps to localStorage (persistent) and sessionStorage (session):

import { configure, browserStorage } from "@agicash/opensecret";

configure({ apiUrl: "{URL}", clientId: "{PROJECT_UUID}", storage: browserStorage });

For non-browser runtimes (React Native, Node, Bun, CLI, MCP), implement the StorageProvider interface against your own backing store. It has two scopes, each a KeyValueStore (getItem / setItem / removeItem). Methods may be synchronous or return a Promise, so asynchronous stores (React Native AsyncStorage, IndexedDB, async SQLite, remote KV) work too:

import { configure, type StorageProvider } from "@agicash/opensecret";

const storage: StorageProvider = {
  // long-lived: auth tokens (access_token, refresh_token); must survive restarts
  persistent: {
    getItem: (key) => myStore.get(key),
    setItem: (key, value) => myStore.set(key, value),
    removeItem: (key) => myStore.delete(key)
  },
  // ephemeral per session: the attestation handshake (sessionKey, sessionId)
  session: {
    getItem: (key) => mySessionStore.get(key),
    setItem: (key, value) => mySessionStore.set(key, value),
    removeItem: (key) => mySessionStore.delete(key)
  }
};

configure({ apiUrl: "{URL}", clientId: "{PROJECT_UUID}", storage });

API Reference

Configuration

configure(options)

Configures the OpenSecret SDK with your API URL, client ID, and storage provider. Must be called before using any other SDK functions.

configure({
  apiUrl: string,           // The URL of your OpenSecret backend
  clientId: string,         // A UUID that identifies your project/tenant
  storage: StorageProvider  // Where the SDK persists tokens + session (see Storage)
})

Example:

import { configure, browserStorage } from '@agicash/opensecret';

configure({
  apiUrl: 'https://api.opensecret.cloud',
  clientId: '550e8400-e29b-41d4-a716-446655440000',
  storage: browserStorage
});

Direct API Functions

All functions can be imported directly from the package:

Authentication Methods

import { signIn, signUp, signInGuest, signUpGuest, convertGuestToUserAccount, signOut } from '@agicash/opensecret';

// Sign in with email/password
await signIn(email: string, password: string);

// Sign up new user
await signUp(email: string, password: string, inviteCode: string, name?: string);

// Guest authentication
await signInGuest(id: string, password: string);
const { id, access_token, refresh_token } = await signUpGuest(password: string, inviteCode: string);

// Convert guest to full account
await convertGuestToUserAccount(email: string, password: string, name?: string);

// Sign out
await signOut();

Key-Value Storage Methods

import { get, put, list, del } from '@agicash/opensecret';

// Get a value
const value = await get(key: string);

// Store a value  
await put(key: string, value: string);

// List all key-value pairs
const items = await list();

// Delete a value
await del(key: string);

User Management

import { fetchUser, changePassword, generateThirdPartyToken } from '@agicash/opensecret';

// Get current user
const user = await fetchUser();

// Change password
await changePassword(currentPassword: string, newPassword: string);

// Generate third-party JWT token
const { token } = await generateThirdPartyToken(audience?: string);

Cryptographic Methods

Key Derivation Options

For cryptographic operations, the SDK supports a KeyOptions object with the following structure:

type KeyOptions = {
  /** 
   * BIP-85 derivation path to derive a child mnemonic
   * Example: "m/83696968'/39'/0'/12'/0'"
   */
  seed_phrase_derivation_path?: string;
  
  /**
   * BIP-32 derivation path to derive a child key from the master (or BIP-85 derived) seed
   * Example: "m/44'/0'/0'/0/0"
   */
  private_key_derivation_path?: string;
};

All cryptographic methods accept this KeyOptions object as a parameter to specify derivation options.

Methods
  • getPrivateKey(key_options?: KeyOptions): Promise<{ mnemonic: string }>: Retrieves the user's private key mnemonic phrase.

    • If no key_options are provided, returns the master mnemonic
    • If seed_phrase_derivation_path is provided, returns a BIP-85 derived child mnemonic
    • For BIP-85, the path format is typically m/83696968'/39'/0'/12'/0' where:
      • 83696968' is the hardened BIP-85 application number (ASCII for "BIPS")
      • 39' is the hardened BIP-39 application (for mnemonic derivation)
      • 0' is the hardened coin type (0' for Bitcoin)
      • 12' is the hardened entropy in words (12-word mnemonic)
      • 0' is the hardened index (can be incremented to generate different phrases)
  • getPrivateKeyBytes(key_options?: KeyOptions): Promise<{ private_key: string }>: Retrieves the private key bytes with flexible derivation options.

    • Supports multiple derivation approaches:
    1. Master key only (no parameters)

      • Returns the master private key bytes
    2. BIP-32 derivation only

      • Uses path format like m/44'/0'/0'/0/0
      • Supports both absolute (starting with "m/") and relative paths
      • Supports hardened derivation using either ' or h notation
    3. BIP-85 derivation only

      • Derives a child mnemonic from the master seed using BIP-85
      • Then returns the master private key of that derived seed
    4. Combined BIP-85 and BIP-32 derivation

      • First derives a child mnemonic via BIP-85
      • Then applies BIP-32 derivation to that derived seed

    Common BIP-32 paths:

    • BIP44 (Legacy): m/44'/0'/0'/0/0
    • BIP49 (SegWit): m/49'/0'/0'/0/0
    • BIP84 (Native SegWit): m/84'/0'/0'/0/0
    • BIP86 (Taproot): m/86'/0'/0'/0/0
  • getPublicKey(algorithm: 'schnorr' | 'ecdsa', key_options?: KeyOptions): Promise<PublicKeyResponse>: Retrieves the user's public key for the specified signing algorithm and derivation options.

    The derivation paths determine which key is used to generate the public key:

    • Master key (no parameters)
    • BIP-32 derived key
    • BIP-85 derived key
    • Combined BIP-85 + BIP-32 derived key

    Supports two algorithms:

    • 'schnorr': For Schnorr signatures
    • 'ecdsa': For ECDSA signatures
  • signMessage(messageBytes: Uint8Array, algorithm: 'schnorr' | 'ecdsa', key_options?: KeyOptions): Promise<SignatureResponse>: Signs a message using the specified algorithm and derivation options.

    Example message preparation:

    // From string
    const messageBytes = new TextEncoder().encode("Hello, World!");
    
    // From hex
    const messageBytes = new Uint8Array(Buffer.from("deadbeef", "hex"));
  • encryptData(data: string, key_options?: KeyOptions): Promise<{ encrypted_data: string }>: Encrypts arbitrary string data using the user's private key with flexible derivation options.

    Examples:

    // Encrypt with master key
    const { encrypted_data } = await encryptData("Secret message");
    
    // Encrypt with BIP-32 derived key
    const { encrypted_data } = await encryptData("Secret message", {
      private_key_derivation_path: "m/44'/0'/0'/0/0"
    });
    
    // Encrypt with BIP-85 derived key
    const { encrypted_data } = await encryptData("Secret message", {
      seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'"
    });
    
    // Encrypt with combined BIP-85 and BIP-32 derivation
    const { encrypted_data } = await encryptData("Secret message", {
      seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'",
      private_key_derivation_path: "m/44'/0'/0'/0/0"
    });
  • decryptData(encryptedData: string, key_options?: KeyOptions): Promise<string>: Decrypts data that was previously encrypted with the user's key.

    IMPORTANT: You must use the exact same derivation options for decryption that were used for encryption.

    Examples:

    // Decrypt with master key
    const decrypted = await decryptData(encrypted_data);
    
    // Decrypt with BIP-32 derived key
    const decrypted = await decryptData(encrypted_data, {
      private_key_derivation_path: "m/44'/0'/0'/0/0"
    });
    
    // Decrypt with BIP-85 derived key
    const decrypted = await decryptData(encrypted_data, {
      seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'"
    });
    
    // Decrypt with combined BIP-85 and BIP-32 derivation
    const decrypted = await decryptData(encrypted_data, {
      seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'",
      private_key_derivation_path: "m/44'/0'/0'/0/0"
    });
Implementation Examples
  1. Basic Usage with Default Master Key
// Get the master mnemonic
const { mnemonic } = await getPrivateKey();

// Get the master private key bytes
const { private_key } = await getPrivateKeyBytes();

// Sign with the master key
const signature = await signMessage(messageBytes, 'ecdsa');
  1. Using BIP-32 Derivation Only
// Get private key bytes using BIP-32 derivation
const { private_key } = await getPrivateKeyBytes({
  private_key_derivation_path: "m/44'/0'/0'/0/0"
});

// Sign with a derived key
const signature = await signMessage(messageBytes, 'ecdsa', {
  private_key_derivation_path: "m/44'/0'/0'/0/0"
});
  1. Using BIP-85 Derivation Only
// Get a child mnemonic phrase derived via BIP-85
const { mnemonic } = await getPrivateKey({
  seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'"
});

// Get master private key of a BIP-85 derived seed
const { private_key } = await getPrivateKeyBytes({
  seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'"
});
  1. Using Both BIP-85 and BIP-32 Derivation
// Get private key bytes derived through BIP-85 and then BIP-32
const { private_key } = await getPrivateKeyBytes({
  seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'",
  private_key_derivation_path: "m/44'/0'/0'/0/0"
});

// Sign a message with a key derived through both methods
const signature = await signMessage(messageBytes, 'schnorr', {
  seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'",
  private_key_derivation_path: "m/44'/0'/0'/0/0"
});
  1. Encryption/Decryption with Derived Keys
// Encrypt with a BIP-85 derived key
const { encrypted_data } = await encryptData("Secret message", {
  seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'"
});

// Decrypt using the same derivation path
const decrypted = await decryptData(encrypted_data, {
  seed_phrase_derivation_path: "m/83696968'/39'/0'/12'/0'"
});

AI Integration

To get encrypted-to-the-gpu AI chat, use the createAiCustomFetch function to create a special version of fetch that handles all the encryption. Because we require the user to be logged in, and do the encryption client-side, this is safe to call from the client.

The easiest way to use this is through the OpenAI client:

npm install openai
import OpenAI from "openai";
import { configure, createAiCustomFetch, getConfig } from "@agicash/opensecret";

// Configure the SDK
configure({
  apiUrl: "https://api.opensecret.cloud",
  clientId: "your-project-uuid"
});

// Create the OpenAI client with encrypted fetch
const openai = new OpenAI({
  baseURL: `${getConfig().apiUrl}/v1/`,
  dangerouslyAllowBrowser: true,
  apiKey: "api-key-doesnt-matter", // The actual API key is handled by OpenSecret
  defaultHeaders: {
    "Accept-Encoding": "identity",
    "Content-Type": "application/json",
  },
  fetch: createAiCustomFetch(), // Use OpenSecret's encrypted fetch
});

// Use the OpenAI client as normal
const completion = await openai.chat.completions.create({
  model: "gpt-4",
  messages: [{ role: "user", content: "Hello!" }],
  stream: true
});

You can now use the OpenAI client as normal. (Right now only streaming responses are supported.)

For an alternative approach using createAiCustomFetch directly, see the integration test at src/lib/test/integration/ai.test.ts in the SDK source code.

Library development

This library uses Bun for development.

Install dependencies:

bun install

To build the library, run the following command:

bun run build

To test the library, run the following command:

bun test --env-file .env.local

To test a specific file or test case:

bun test --test-name-pattern="Developer login and token storage" src/lib/test/integration/developer.test.ts --env-file .env.local

Currently this build step requires npx because of a Bun incompatibility with vite-plugin-dts.

To pack the library (for publishing) run the following command:

bun run pack

To deploy:

NPM_CONFIG_TOKEN=$NPM_CONFIG_TOKEN bun publish --access public

Documentation Development

The SDK documentation is built using Docusaurus, a modern documentation framework. The documentation is automatically generated from TypeScript code comments and supplemented with manually written guides.

Getting Started with Documentation

To start the documentation development server:

bun run docs:dev

This will start the Docusaurus development server and open the documentation in your browser at http://localhost:3000/. The server supports hot-reloading, so any changes you make to the documentation will be immediately reflected in the browser.

Building Documentation

To build the documentation for production:

bun run docs:build

This will generate static HTML, JavaScript, and CSS files in the website/build directory.

To serve the built documentation locally:

bun run docs:serve

Documentation Structure

The documentation is organized into the following directories:

  • /website/docs/ - Contains all manual documentation files
    • index.md - The documentation landing page
    • /guides/ - Step-by-step guides for using the SDK
    • /api/ - API reference documentation (mostly auto-generated)

API Reference Documentation

The API reference documentation is automatically generated from TypeScript code comments using TypeDoc. To update the API documentation:

  1. Write proper JSDoc comments in the TypeScript source code
  2. Run bun run docs:build to regenerate the documentation

Important notes for API documentation:

  • Use standard JSDoc syntax for documenting parameters, return types, and descriptions
  • For Markdown in JSDoc comments, be aware that backticks (`) must be properly escaped
  • For code examples with apostrophes (e.g., BIP paths like m/44'/0'/0'/0/0), use backslash escaping: m/44\'/0\'/0\'/0/0

Adding New Guides

To add a new guide:

  1. Create a new Markdown file in the /website/docs/guides/ directory
  2. Add frontmatter at the top of the file:
    ---
    title: Your Guide Title
    sidebar_position: X  # Controls the order in the sidebar
    ---
  3. Update the sidebar configuration in /website/sidebars.ts if needed

Customizing the Documentation

The main configuration files for Docusaurus are:

  • /website/docusaurus.config.ts - Main Docusaurus configuration
  • /website/sidebars.ts - Sidebar configuration
  • /website/typedoc.json - TypeDoc configuration for API docs

To customize the appearance:

  • Edit /website/src/css/custom.css for global styles
  • Create or modify components in /website/src/components/

Deployment

The documentation can be deployed to various platforms like GitHub Pages, Netlify, or Vercel. For CloudFlare Pages deployment, as mentioned in our guideline:

  1. In CloudFlare Pages, create a new project connected to your GitHub repo
  2. Use these build settings:
    • Build command: cd website && bun run build
    • Build output directory: website/build
  3. Set up a custom domain through CloudFlare's dashboard

Troubleshooting

Common issues:

  • If TypeDoc fails to generate documentation, check the JSDoc comments for syntax errors
  • If you see "Could not parse expression with acorn" errors, there are likely unescaped characters in code examples
  • If links are broken, check that the referenced pages exist and paths are correct
  • For sidebar issues, verify that the sidebar configuration in sidebars.ts is correct

License

This project is licensed under the MIT License.

About

Agicash fork of OpenSecret-SDK

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • TypeScript 63.9%
  • Rust 33.2%
  • CSS 1.4%
  • JavaScript 0.6%
  • Nix 0.5%
  • Shell 0.3%
  • MDX 0.1%