@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-baseYou 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-sdkAppSdk
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
| Method | Description |
|---|---|
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 }:
- If
nameis a key inschema.tableMap→ the enclave is found by the event name in the map's value. - Else if
namematches an enclave event directly (fallback) → that enclave + event. - 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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
fromforShared(<key>)slot events — a keyed Map. - UPSERT keyed by id field for registry snapshots (
reg_identity→id_pub,reg_enclave→enclave_id,reg_node→seq_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
- Per-app SDKs — the generated subclasses for each reference app
@enc-protocol/cli— theencbinary that uses these classes internally@enc-protocol/client— the per-enclave adapter theAppClientwraps