Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

@enc-protocol/cli-sdk-base — base SDK classes

The base classes that every @enc-protocol/<app>-cli package extends.

Install

npm config set @enc-protocol:registry https://npm-registry.ocrybit.workers.dev/
npm install @enc-protocol/cli-sdk-base

You usually consume this transitively via a per-app SDK like @enc-protocol/group-cli. Direct use is for advanced cases — building a new app SDK, or embedding the engine in custom tooling.

Exports

import { AppSdk }    from '@enc-protocol/cli-sdk-base/app-sdk'
import { AppClient } from '@enc-protocol/cli-sdk-base/app-client'
import { DataView, loadDataView } from '@enc-protocol/cli-sdk-base/dataview'

Or the barrel:

import { AppSdk } from '@enc-protocol/cli-sdk-base'   // = app-sdk

AppSdk

App-driven SDK class. Reads apps/<id>/{app.json, schema.json} at runtime, composes an AppClient, and adds app-level submit/query that resolve via tableMap to enclave events.

const sdk = new AppSdk({
  appId: 'super',
  mode: 'mem',
  repoRoot: '/path/to/app/repo',
})
await sdk.init()
await sdk.submit('moments', { body: 'wow' })   // → Personal.public
const events = await sdk.query('moments')

Constructor

new AppSdk(opts: {
  appId: string                // matches apps/<id>/
  mode: 'mem' | 'cf'
  repoRoot: string             // path to find apps/ + enclaves/
  identity?: Identity          // required for cf
  nodeUrl?: string             // cf mode
  encHome?: string             // state dir; defaults to ~/.enc
  forceReadable?: boolean      // test-only Public-R escape (cf)
})

Methods

MethodDescription
async init()Load app definition, register all enclaves, wire the dataview.
async submit(name, args)Submit. name is a data_type (resolved via tableMap) OR an enclave event directly.
async query(name)Query. cross_enclave: true reads go to the in-memory DataView; others resolve via tableMap.
raw()Returns the underlying AppClient for low-level access.
whoami()Identity + registered enclaves + dataview info.

Resolution

_resolve(name) returns { enclave, event }:

  1. If name is a key in schema.tableMap → the enclave is found by the event name in the map's value.
  2. Else if name matches an enclave event directly (fallback) → that enclave + event.
  3. Otherwise throws.

AppClient

Multi-enclave coordinator. Holds one identity and N enclave adapters.

const client = new AppClient({ appId: 'super', mode: 'mem', repoRoot })
await client.init()   // registers all 3 enclaves: DM, Group, Personal
await client.submit('Personal', 'public', { body: 'wow' })

Methods

MethodDescription
async init()Read apps/<id>/app.json, register each enclave.
async addEnclave(name, opts?)Add an enclave manually (init does this automatically).
async submit(enclaveName, event, args)Write to one enclave's adapter.
async query(enclaveName, event)Read from one enclave.
whoami()Identity, mode, list of registered enclaves with ids.

In mem mode, an identity is provisioned per enclave. In cf mode, an externally-supplied identity is used for all enclaves; each is minted on the node with that identity as owner.

DataView

In-memory dataview for cross_enclave: true reads. Mirrors the production Cloudflare Durable Object dataview semantics without SQLite.

Loading

const dataview = loadDataView('super', '/path/to/app/repo')
// Reads apps/super/infra.json's manifest.cross_enclave_reads
// e.g. { profiles: { from: 'Personal.Shared(profile)', via: 'dataview', key: 'id_pub' } }

Methods

MethodDescription
has(viewName)Whether this view is configured.
ingest(enclaveName, ev)Called by AppClient.submit for every successful event; routes to matching views.
query(viewName)Returns rows.
watchedEnclaves()Set of enclave names this dataview cares about.

Storage shape

  • Append-only by default (most events) — stored as an array.
  • UPSERT keyed by from for Shared(<key>) slot events — a keyed Map.
  • UPSERT keyed by id field for registry snapshots (reg_identityid_pub, reg_enclaveenclave_id, reg_nodeseq_pub).

Per-app subclassing

A per-app SDK (@enc-protocol/group-cli, etc.) extends AppSdk:

import { AppSdk } from '@enc-protocol/cli-sdk-base'
 
export class GroupSdk extends AppSdk {
  constructor(opts) { super({ ...opts, appId: 'group' }) }
 
  async _encrypt(dataType, args) { return args }   // default pass-through
 
  async submitMessages(args) {
    args = await this._encrypt('messages', args)
    return this.submit('messages', args)
  }
 
  async queryMessages() { return this.query('messages') }
}

The _encrypt(dataType, args) hook is per-app: pass-through by default; subclass to add MLS / AEAD / your-protocol encryption.

See also