@enc-protocol/app-sdk-base
v0.9.0
Published
Platform-agnostic base SDK for per-app ENC protocol clients. AppSdk + AppClient + DataView. Manifests + adapter injected by caller.
Maintainers
Readme
@enc-protocol/app-sdk-base
Platform-agnostic base SDK for per-app ENC protocol clients. Provides the AppSdk class that every per-app SDK extends, with AppClient multi-enclave coordination, plugin registry wiring, and cross-enclave read views.
Install
npm install @enc-protocol/app-sdk-baseAPI
AppSdk (main export)
new AppSdk(opts) — Initialize the app SDK.
opts.manifests(required):{ app, schema, infra?, enclaves: {...} }opts.adapter: Single shared adapter (shorthand for single-enclave apps)opts.adapters: Map of{ EncleaveName: adapter }for multi-enclave appsopts.plugins: ClientPluginRegistry instance (defaults todefaultClientPluginRegistry())
Resolves data_types and read views to enclave events via schema.tableMap, dispatching to the appropriate enclave's adapter.
await appSdk.init() → this — Initialize the SDK. Currently a no-op; reserved for future async setup.
await appSdk.submit(name, args) → result — Submit a write event. name may be a data_type or enclave event; resolved and routed to the owning enclave's adapter.
await appSdk.query(name) → rows — Query a read view or enclave event. Cross-enclave reads route to the DataView; single-enclave queries dispatch to the adapter.
whoami() → { appId, pubHex, schemaName, dataTypes, reads, enclaves } — Introspection: app identity, schema name, declared data types and reads, and enclave roster.
raw() → AppClient — Access the underlying AppClient for primitive operations.
AppClient
new AppClient(opts) — Initialize the multi-enclave coordinator.
opts.manifests(required):{ app, schema, infra?, enclaves: {...} }opts.adapteroropts.adapters: Adapter(s) for each enclaveopts.identity: Optional{ pubHex }identityopts.plugins: ClientPluginRegistry (defaults todefaultClientPluginRegistry())
await appClient.init() → this — Symmetry hook; currently a no-op.
await appClient.submit(enclaveName, event, args) → result — Submit to a specific enclave. Automatically ingests into DataView if cross-enclave reads are configured.
await appClient.query(enclaveName, event, opts) → result — Query a specific enclave event.
await appClient.grant(enclaveName, target, role) → result — Grant a role to a target.
await appClient.revoke(enclaveName, target, role) → result — Revoke a role.
await appClient.move(enclaveName, target, from, to) → result — Move a target between states.
await appClient.transfer(enclaveName, target, trait) → result — Transfer a trait.
await appClient.queryMembers(enclaveName) → array — List enclave members (if adapter supports it).
whoami() → { appId, pubHex, enclaves: [...] } — Introspection: app and enclave identity.
DataView
new DataView(crossReads, opts) — Initialize cross-enclave read view storage.
crossReads: Object mapping view names to{ from, via, key, access }(frominfra.json'scross_enclave_reads)opts.ingestFn: IngestStrategyFn override (default: plugin-client-base/ingest)opts.initStorageFn: Storage initialization override
dataview.query(viewName) → array | null — Query a cross-enclave read. Returns rows in reverse chronological order (append-only) or as a Map snapshot (for UPSERT views).
dataview.ingest(enclaveName, event) — Internal: ingest an event from a watched enclave. Delegates to the IngestStrategyFn plugin.
dataview.watchedEnclaves() → Set — Return the set of source enclaves this view consumes.
dataview.has(viewName) → boolean — Check if a view is configured.
dataViewFromInfra(infraManifest, opts) → DataView — Factory: build a DataView from an infra manifest.
Plugin Registry & Encryption
defaultClientPluginRegistry() → ClientPluginRegistry — Create a registry pre-bound with protocol-level client slots from @enc-protocol/plugin-client-base.
await registerDmRatchetEncryption(plugins, { getEpochSecret }) → plugins — Wire the ENC protocol's Ratchet_DM encryption (secp256k1 + HKDF-SHA256 + XChaCha20-Poly1305) into a ClientPluginRegistry. Replaces the pass-through EnvelopeEncryptFn/DecryptFn defaults. Lazy-imports @enc-protocol/plugin-dm-ratchet.
Protected Methods (for subclass overrides)
await sdk._encrypt(dataType, args, opts) → args — Envelope-encrypt hook. Default: pass-through via EnvelopeEncryptFn plugin. Subclasses override for real encryption.
await sdk._decrypt(eventType, content, fromPub) → content — Envelope-decrypt hook. Pairs with _encrypt. Default: pass-through.
Example
import { AppSdk, defaultClientPluginRegistry } from '@enc-protocol/app-sdk-base'
// Load manifests (typically baked into per-app SDK)
const manifests = {
app: { id: 'hello', enclaves: ['Hello'] },
schema: { name: 'HelloSchema', data_types: {...}, reads: {...}, tableMap: {...} },
enclaves: {
Hello: { manifest: {...} }
}
}
// Create adapter (e.g., relay, local enclave simulation, etc.)
const adapter = {
submit: async (event, args) => ({ id: '123', ...args }),
query: async (event, opts) => []
}
// Initialize and use
const sdk = new AppSdk({
manifests,
adapter,
plugins: defaultClientPluginRegistry()
})
await sdk.init()
// Submit a write
const result = await sdk.submit('message', { text: 'Hello' })
// Query a read
const rows = await sdk.query('messages')
// Introspect
console.log(sdk.whoami())
// { appId: 'hello', pubHex: null, schemaName: 'HelloSchema', dataTypes: [...], reads: [...], ... }