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/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 enclave

Return 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"}'