@x12i/memorix-nexum-adapter
First-party Memorix host for @x12i/nexum.
| serviceId | nexum |
| operation | analyze |
| envelope | memorix-capability/1 |
| payload | x12i-nexum/1 |
| write-record | portConfig.mode: "nexum" |
| bin | nexum-worker |
npm install @x12i/memorix-nexum-adapter
Node 18+. The capability returns a draft + apply plan. It does not persist. Apply with Memorix write-record mode nexum, or with in-process NexumStore.
Monorepo copy-paste guide: repository root README.md.
Worker
npx nexum-worker
import { createNexumWorker } from "@x12i/memorix-nexum-adapter";
const worker = createNexumWorker();
const { url } = await worker.start();
console.log(url); // http://127.0.0.1:7500/
The CLI (nexum-worker / npm run worker) also binds the ACL read API on port 7502.
| Env | Default |
|---|---|
MEMORIX_NEXUM_WORKER_HOST |
127.0.0.1 |
MEMORIX_NEXUM_WORKER_PORT |
7500 |
MEMORIX_NEXUM_READ_HOST |
worker host |
MEMORIX_NEXUM_READ_PORT |
7502 |
MEMORIX_NEXUM_READ_URL |
http://127.0.0.1:7502 |
MEMORIX_NEXUM_READ_CORS_ORIGINS |
empty / deny |
MEMORIX_NEXUM_LOG_LEVEL |
info |
MEMORIX_NEXUM_PROFILE |
deterministic |
MEMORIX_NEXUM_DEV_MEMORY_STORE |
unset (forbidden in production) |
MEMORIX_NEXUM_DEV_PROFILE |
unset (forbidden in production) |
Capability invoke
import { invokeCapability } from "@x12i/memorix-sdk/worker";
import { buildCapabilityInvocation } from "@x12i/memorix-sdk/testing";
import { nexumCapability, SERVICE_ID, OPERATION } from "@x12i/memorix-nexum-adapter";
import type { NexumAnalyzeResult } from "@x12i/nexum";
const out = (await invokeCapability(
nexumCapability,
buildCapabilityInvocation({
serviceId: SERVICE_ID,
operation: OPERATION,
inputs: {
organizationId: "acme",
items: [
{
id: "credorix",
objectType: "product",
data: { name: "Credorix", ownerTeamId: "identity-team" },
},
{
id: "auth-doc",
inputType: "content",
title: "Auth",
format: "markdown",
content: "# Auth\n\nSee [Credorix](memorix://product/credorix).\n",
},
],
},
}),
)) as NexumAnalyzeResult;
curl -sS http://127.0.0.1:7500/ \
-H 'content-type: application/json' \
-d '{
"version": "memorix-capability/1",
"serviceId": "nexum",
"operation": "analyze",
"scope": { "orgId": "acme", "agentIds": ["opx"] },
"execution": {
"runId": "run-1",
"stepId": "analyze",
"attempt": 1,
"correlationId": "run-1:analyze:1"
},
"inputs": {
"items": [
{ "id": "credorix", "objectType": "product", "data": { "name": "Credorix" } }
]
}
}'
Sync cap: 100 items or 2 MiB → NEXUM_ASYNC_REQUIRED. memorix:// links are resolved in this adapter, not in core.
Pipeline
{
"steps": [
{ "stepId": "analyze", "stepKind": "analyze", "serviceId": "nexum" },
{
"stepId": "write-graph",
"stepKind": "write-record",
"contentType": "nexum",
"portConfig": { "mode": "nexum" },
"dependsOn": ["analyze"]
}
],
"services": [
{ "serviceId": "nexum", "kind": "worker", "endpoint": "http://127.0.0.1:7500/" }
]
}
Pack fragment: metadata/services.json (export ./metadata/*).
import { translateApplyPlan, WRITE_RECORD_MODE } from "@x12i/memorix-nexum-adapter";
WRITE_RECORD_MODE; // "nexum"
const records = translateApplyPlan(result.scope, result.applyPlan);
// nexum.contribution/v1, nexum.node/v1, nexum.node-claim/v1, …
Local store + read API
import { analyze, analyzeRequest, structuredItem } from "@x12i/nexum";
import {
NexumStore,
createNexumReadApp,
createNexumClient,
dropUnchangedItems,
} from "@x12i/memorix-nexum-adapter";
const scope = { organizationId: "acme", graphId: "default" };
const store = new NexumStore();
const result = await analyze({
request: analyzeRequest([
structuredItem("credorix", "product", { name: "Credorix", ownerTeamId: "identity-team" }),
]),
});
store.applyResult(scope, result);
store.rebuildProjection(scope);
const { request, skipped } = dropUnchangedItems(
store,
analyzeRequest([
structuredItem("credorix", "product", { name: "Credorix", ownerTeamId: "identity-team" }),
]),
);
const app = createNexumReadApp({ store, defaultOrg: "acme" });
const baseUrl = await app.listen({ port: 7502, host: "127.0.0.1" });
const client = createNexumClient({
baseUrl,
orgId: "acme",
headers: { "x-nexum-grants": "*", "x-nexum-admin": "true" },
});
const entities = await client.listEntities({ type: "product" });
const nodeId = entities.items[0]!.nodeId;
await client.getEntity(nodeId);
await client.queryGraph({ seeds: [{ nodeId }], depth: 1 });
await client.why({ kind: "node", id: nodeId });
await client.listSignals();
await client.exportJson();
await app.close();
SDK from env (MEMORIX_NEXUM_READ_URL, NEXUM_CLI_DEFAULT_ORG_ID, NEXUM_CLI_DEFAULT_GRAPH_ID):
import { createNexumClientFromEnv } from "@x12i/memorix-nexum-adapter/client";
const client = createNexumClientFromEnv();
Read API
Base path: /nexum/v1/orgs/{orgId}/graphs/{graphId}
| Method | Path | SDK |
|---|---|---|
| GET | /entities?type=&q=&cursor= |
listEntities |
| GET | /entities/:nodeId |
getEntity |
| POST | /graph/query |
queryGraph |
| POST | /connect |
connect |
| POST | /why |
why |
| GET | /signals |
listSignals |
| GET | /reviews |
listReviews |
| POST | /reviews/:id/decisions |
decideReview |
| GET | /revisions/current |
getCurrentRevision |
| GET | /statistics |
getStatistics |
| POST | /exports/json |
exportJson |
Headers: x-nexum-org, x-nexum-grants (* or comma-separated refs), x-nexum-admin: true, x-nexum-actor.
Reads are assembled from visible claims only. A hidden name cannot appear in labels, search, why, export, or communities.
curl -sS 'http://127.0.0.1:7502/nexum/v1/orgs/acme/graphs/default/entities?type=product' \
-H 'x-nexum-org: acme' \
-H 'x-nexum-grants: *' \
-H 'x-nexum-admin: true'
Review decision:
await client.decideReview(candidateId, {
kind: "MERGE_IDENTITY",
survivorNodeId,
memberNodeIds,
});
Public exports
nexumCapability, createNexumWorker, createNexumReadApp, loadAdapterConfig, NexumStore, dropUnchangedItems, translateApplyPlan, createNexumClient, createNexumClientFromEnv, MEMORIX_URI_RESOLVER, SERVICE_ID, OPERATION, WRITE_RECORD_MODE.