@enc-protocol/memory — API Reference
@enc-protocol/memory runs the entire ENC node protocol in the JavaScript heap — no
network, no subprocess, no fetch. It exposes the same surface an app uses against a real
node (createEnclave / submit / query / subscribe / as / toEnclave)
but executes the node state machine directly in-process: same RBAC, same signature
validation, same event log, just no wire. It's the fast path for tests, local development,
and deterministic workflows. Like the other SDKs, it's generated from the Lean specification.
Installation
npm install @enc-protocol/memory --registry https://npm-registry.ocrybit.workers.dev/MemoryAdapter
A single class. Construct one bound to an identity, then drive enclaves through it. Multiple
adapters in a process share one in-memory registry of enclaves keyed by enclave id, so
as() / toEnclave() and cross-user reads see consistent state — mirroring how one node
hosts many enclaves.
MemoryAdapter.devMode(nodeUrl, enclaveId, privKeyHex, opts?)
Create an adapter with a signing key (the common entry point for tests).
import { MemoryAdapter } from '@enc-protocol/memory'
const alice = MemoryAdapter.devMode(null, null, alicePrivHex)Derives the public key from privKeyHex and returns an adapter that can sign commits.
nodeUrl is ignored (there is no network) and enclaveId may be null until you create or
attach to one.
.createEnclave(manifest)
await adapter.createEnclave(manifest: string | Object)
→ { enclave_id: string, ok: true } | { error: string }Signs a Manifest commit, instantiates a fresh in-heap node, and returns the derived enclave
id (the manifest hash). The adapter's enclaveId is set to the new enclave.
.submit(type, content)
await adapter.submit(type: string, content: string | Object)
→ { ok: true, ...receipt } | { error: string, type: 'Error' }Signs and applies a commit of the given event type. Runs the same RBAC authorization and signature checks as a real node; on success returns the receipt and notifies subscribers.
.query(type, opts?)
await adapter.query(type: string, opts?: { limit?: number, from?: string, reverse?: boolean })
→ event[]Returns events from the current enclave, filtered by type ('*' for all) and optionally by
author, newest-first by default.
.subscribe(callback)
const unsubscribe = adapter.subscribe((event) => { /* … */ })Registers a listener for new events on the current enclave and returns an unsubscribe
function. submit delivers each accepted event to all subscribers synchronously.
RBAC helpers
await adapter.grant(target, role) // Grant(<role>)
await adapter.revoke(target, role) // Revoke(<role>)
await adapter.move(target, fromState, toState) // Move(<from>,<to>)
await adapter.transfer(target, trait) // Transfer(<trait>)Convenience wrappers over submit that build the corresponding access-control event.
.as(pubHex) / .toEnclave(enclaveId)
adapter.as(pubHex: string) → MemoryAdapter // same enclave + signer, different identity view
adapter.toEnclave(enclaveId: string) → MemoryAdapter // same signer, different enclaveReturn new adapters that share the signing key and the in-memory registry, so you can act as another identity or talk to another enclave without re-creating state.
.queryEndpoint(path, opts?)
await adapter.queryEndpoint(path: string, opts?: { limit?: number, reverse?: boolean })
→ row[]Emulates a public dataview: scans every enclave in the registry for events matching the
path's event name and returns flattened rows
({ ...content, from, _enclave, _event_id, _timestamp }), newest-first. This is the
in-memory stand-in for a node's cross-enclave indexing, so SDK code that reads from a dataview
at runtime works unchanged against memory.
MemoryAdapter.reset()
MemoryAdapter.reset()Wipe the shared in-memory registry (enclaves, subscriptions, any persisted state) between test runs.
Example
import { MemoryAdapter } from '@enc-protocol/memory'
MemoryAdapter.reset()
// Alice creates an enclave and posts
const alice = MemoryAdapter.devMode(null, null, alicePrivHex)
const { enclave_id } = await alice.createEnclave({
enc_v: 2, nonce: 1,
RBAC: {
use_temp: 'none',
schema: [
{ event: 'post', role: 'owner', ops: ['C', 'U', 'D'] },
{ event: '*', role: 'Public', ops: ['R'] },
],
states: [], traits: ['owner(0)'],
initial_state: { owner: [alice.pubHex] },
},
})
await alice.submit('post', { body: 'hello from the heap' })
// Bob reads it — same enclave, different identity, no network
const bob = alice.as(bobPubHex)
const posts = await bob.query('post')
console.log(posts[0].content) // '{"body":"hello from the heap"}'