npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@x12i/memorix-explorer-api

v1.37.0

Published

HTTP API for Memorix Explorer — thin adapters over @x12i/memorix-retrieval, with read-only descriptor metadata routes and optional Authix auth.

Readme

@x12i/memorix-explorer-api

HTTP API for Memorix Explorer — a Fastify server that exposes read and write endpoints over MongoDB and Catalox descriptors.

This package is a thin HTTP layer on top of:

| Package | Role in this API | |---------|------------------| | @x12i/memorix-retrieval | All reads — inventory, lists, items, narratives, agents, health | | @x12i/memorix-associator | Associated snapshot property metadata and storage-to-client associated aliases | | @x12i/memorix-descriptors | List descriptor mutations; entity descriptor narratives catalog + root property catalog compute | | @x12i/memorix-writer | Record writes via write descriptors |

The React UI lives in @x12i/memorix-explorer (sibling package). You can run this API standalone, embed it in another host, or serve the built Explorer UI from the same process.

Documentation

| Guide | Topic | |-------|-------| | docs/README.md | Documentation index | | Consuming Memorix data | Read-only API — snapshots, records, lists, narratives, inventory, Skills cards | | Advanced APIs | Writes, enrichment, associations, metadata, pipeline registry |


Architecture

Client (browser, curl, automation)
  ↓ HTTP /api/explorer/*
memorix-explorer-api (Fastify)
  ↓ getRetrievalStack()     → @x12i/memorix-retrieval + Catalox + Mongo (Xronox)
  ↓ getDescriptorAdmin()    → @x12i/memorix-descriptors (list writes, narratives catalog, rootPropertyCatalog compute)
  ↓ getWriterClient()       → @x12i/memorix-writer (POST /records/write)
MongoDB cluster (payload + metadata)
  memorix-entities / memorix-events / memorix-knowledge  — record payloads (Xronox)
  memorix-catalox — Catalox descriptor metadata (lists, object types, items)

Descriptor boundary: list descriptors and authored narrative catalog entries on entity descriptors are writable through this API (when MEMORIX_EXPLORER_ENABLE_METADATA_WRITES=true). Full Catalox catalog manage + execute lives in @x12i/memorix-catalox-api (:5180). GET /catalogs* on Explorer returns 410 Gone — use /api/catalox/catalogs instead.


Requirements

  • Node.js ≥ 20
  • MongoDB reachable via MONGO_URI (payload databases + memorix-catalox metadata database)
  • Catalox metadata stored in Mongo (MEMORIX_CATALOX_DB, default memorix-catalox) — same cluster as MONGO_URI
  • Optional: OpenRouter or Gemini API key for AI-assisted list suggestions

Quick start

From this package directory:

npm install

# Create .env with at least MONGO_URI (and optional MEMORIX_CATALOX_DB; see Environment below).
# In the monorepo you can copy from ../memorix-explorer/.env as a starting point.

# Apply Catalox seeds for entity/list/item descriptors (once per app/environment)
npm run catalox:seed:memorix-retrieval:apply

# Start the API server (default port 5181)
npm run dev

Verify:

curl -s http://127.0.0.1:5181/health                                     # open — no token
curl -s http://127.0.0.1:5181/api/explorer/health \
  -H "Authorization: Bearer $EXPLORER_API_TOKEN" | jq '.ok, .memorixDb'   # token needed if EXPLORER_API_TOKEN is set

API base URL (local): http://localhost:5181/api/explorer API base URL (deployed): https://explorer.neociso.com/api/explorer

All routes documented below are relative to that base unless noted. See Authentication for the bearer token.


Authentication

Two modes — Authix (browser + bearer sessions) and static M2M bearer — are mutually exclusive guards on /api/explorer/*.

Authix (recommended for deployed UI)

When AUTHIX_API_URL and AUTHIX_API_KEY are set (or AUTHIX_AUTH_MODE=app / sso), Authix protects all /api/* routes except the public auth allowlist (/api/auth/login, /api/auth/sso/*, etc.). Browser users log in via POST /api/auth/login (password) or SSO; the session is an httpOnly cookie. Machine callers can send Authorization: Bearer <authix-token>.

EXPLORER_API_TOKEN is ignored when Authix is enabled — do not set both unless you intend Authix only.

| Variable | Description | |----------|-------------| | AUTHIX_API_URL | Authix service base URL (e.g. https://authix.x12i.com) | | AUTHIX_API_KEY | Service API key (x-authix-api-key) | | AUTHIX_AUTH_MODE | app (password login), sso, or disabled | | AUTHIX_ADMIN_USERNAME / AUTHIX_ADMIN_PASSWORD | Operator credentials for app mode | | AUTHIX_APP_ID | App id registered in Authix (default memorix-explorer) |

Static M2M bearer (Authix disabled only)

When Authix is disabled (AUTHIX_AUTH_MODE=disabled and no AUTHIX_API_URL), you may set EXPLORER_API_TOKEN to require a static bearer on /api/explorer/* only:

curl -s https://explorer.neociso.com/api/explorer/inventory/summary \
  -H "Authorization: Bearer $EXPLORER_API_TOKEN"
  • Exempt (no token): GET /health, /api/auth/*, and OPTIONS preflight.
  • Missing/incorrect token → 401 { "error": "unauthorized" }.
  • Constant-time compared shared secret — not a JWT. Store in deployment secrets (not in this repo).
  • When EXPLORER_API_TOKEN is unset, /api/explorer/* is open.

Deployed note: explorer.neociso.com sets EXPLORER_API_TOKEN in server environment (outside this repo). If Authix is also configured there, redeploy with this fix so /api/auth/login is reachable again.


Running the server

Development

npm run dev          # tsx src/cli.ts serve — loads .env from cwd or ../memorix-explorer/.env

Production

npm run build
npm run serve        # node dist/cli.js serve

CLI (after build or via npx)

npx memorix-explorer-api serve

Programmatic embedding

import {
  createMemorixExplorerApp,
  startMemorixExplorerHttp,
  EXPLORER_API_PREFIX,
} from "@x12i/memorix-explorer-api";

// Start listening
const { url, close } = await startMemorixExplorerHttp({
  port: 5181,
  publicDir: "/path/to/built/explorer-ui", // optional — serves static SPA + fallback
});
console.log(`Listening on ${url}${EXPLORER_API_PREFIX}`);

// Or integrate into an existing Fastify/host setup
const { app } = await createMemorixExplorerApp({ logger: true });
await app.listen({ port: 5181, host: "0.0.0.0" });

When EXPLORER_PUBLIC_DIR (or publicDir option) points at a built Explorer UI (index.html present), non-API routes serve the SPA; API routes remain under /api/explorer.


Environment variables

Required

| Variable | Description | |----------|-------------| | MONGO_URI | MongoDB connection string for the cluster (alias: MONGO_CONNECTION_STRING) |

Mongo target databases

Override only when your deployment uses non-default database names. See Memorix Database Conventions.

| Variable | Default | Target | |----------|---------|--------| | MEMORIX_ENTITIES_DB | memorix-entities | Entity records (target=entity) | | MEMORIX_EVENTS_DB | memorix-events | Event records (target=event) | | MEMORIX_KNOWLEDGE_DB | memorix-knowledge | Knowledge records (target=knowledge, optional) | | MEMORIX_OPERATIONAL_DB | memorix-operational | Platform ops (*-failed, schema observation, pipeline-registry-parts) | | MEMORIX_PIPELINE_REGISTRY_DB | (falls back to operational) | Optional override for remote pipeline plugin registry only | | MEMORIX_CATALOX_DB | memorix-catalox | Catalox descriptor metadata (lists, object types, items) |

Catalox

| Variable | Default | Description | |----------|---------|-------------| | CATALOX_APP_ID | memorix | Catalox app id (alias: MEMORIX_APP_ID) |

Catalox metadata uses the same MONGO_URI cluster; database name defaults to memorix-catalox.

Server

| Variable | Default | Description | |----------|---------|-------------| | EXPLORER_API_PORT | 5181 | HTTP port (fallback: PORT) | | EXPLORER_API_TOKEN | (unset) | Static M2M bearer on /api/explorer/* when Authix is disabled only. See Authentication | | EXPLORER_PUBLIC_DIR | ./public if present | Path to built Explorer static files |

Optional — AI list suggestions

| Variable | Description | |----------|-------------| | OPENROUTER_API_KEY | Enables richer confidence on GET /lists/suggest?mode=ai | | GEMINI_API_KEY | Alternative AI provider for the same route |

Smoke / test helpers

| Variable | Default | Description | |----------|---------|-------------| | SMOKE_BASE | http://127.0.0.1:5181/api/explorer | Base URL for smoke scripts | | SMOKE_STRICT_INVENTORY | off | Fail inventory smoke if no rows | | SMOKE_STRICT_ENTITIES | off | Fail entity smoke if graph/records empty |


HTTP conventions

Base path and methods

  • Explorer API routes: /api/explorer + path (e.g. /api/explorer/health).
  • Process liveness: GET /health{ "ok": true } (does not check Mongo/Catalox).
  • Supported methods on /api/explorer/*: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS.
  • OPTIONS returns 204 with no body.
  • Responses include Cache-Control: no-store.
  • Request body limit: 1 MB (JSON bodies for POST/PATCH/PUT).

Content type

Send JSON bodies with Content-Type: application/json. Responses are JSON unless serving static UI.

Errors

| Status | Meaning | |--------|---------| | 400 | Missing/invalid query params or JSON body | | 401 | Missing/incorrect bearer token (when EXPLORER_API_TOKEN is set) — body { "error": "unauthorized" } | | 404 | Unknown route or missing list/narrative | | 422 | Write validation failed (POST /records/write) | | 500 | Unexpected server error — body { "error": "..." } |

Pagination

Shared query parameters for list-style endpoints:

| Param | Alias | Default | Description | |-------|-------|---------|-------------| | limit | — | 50 | Page size (clamped 1–500) | | offset | skip | 0 | Rows to skip | | includeTotal | — | false | Include total count in page.total when supported |

Search

| Param | Alias | Description | |-------|-------|-------------| | searchText | q | Full-text search when supported by the underlying list |

Sorting

Repeated sort params (preferred for compound sort):

GET /records/collection?entityName=assets&sort=severity:desc&sort=entityId:asc

Legacy single sort:

| Param | Alias | |-------|-------| | sortProperty | sortPath | | sortDirection | asc or desc |

Filtering

Repeat filter as property:operator:value:

GET /records/collection?entityName=assets&filter=severity:gte:7&filter=status:eq:open

| Operator | Value notes | |----------|-------------| | eq, ne, gt, gte, lt, lte | Scalar comparison | | in, nin | Comma-separated list | | exists | true/false (default true if value omitted) | | regex | String pattern |

Targets

Many routes accept target: entity, event, or knowledge. Omit or use all where documented to include all configured targets.


API reference

Health

GET /health

Memorix-aware health check — Mongo bindings, Catalox discovery, warnings, and optional inventory summary.

| Query | Description | |-------|-------------| | includeInventory | 1 / true — attach inventory summary counts |

Example:

curl -s "$BASE/health?includeInventory=1"

Response highlights:

{
  "ok": true,
  "generatedAt": "2026-07-01T12:00:00.000Z",
  "mongoUriConfigured": true,
  "memorixDb": "memorix-entities + memorix-events",
  "discoverySample": ["assets", "vulnerabilities"],
  "sourceAware": { "...": "full retrieval health object" },
  "retrieval": {
    "appId": "memorix",
    "entitiesCatalogId": "memorix-object-type-descriptors",
    "discoverySource": "catalox",
    "catalogFallback": false
  },
  "inventorySummary": {
    "orphans": 0,
    "declaredEmpty": 1,
    "matchedPopulated": 12,
    "missingCollections": 0
  },
  "warnings": [],
  "errors": []
}

Use this endpoint before debugging empty lists or inventory issues.


Inventory

Unified inventory and entity graph for Explorer navigation.

GET /inventory/collections · GET /inventory/summary

Same handler — returns MemorixUnifiedInventory rows, issues, and per-target summaries.

| Query | Description | |-------|-------------| | sourceLens | catalox-first (descriptor-first) or db-first (Mongo-first). Default: db-first | | target | entity, event, knowledge, or all | | includeExactCounts | 1 / true — run exact countDocuments (slower) | | includeIgnored | Include collections marked ignored in inventory policy |

Example:

curl -s "$BASE/inventory/collections?sourceLens=db-first&includeExactCounts=1"

Each row includes provenance (source, sources), suggested UI actions (actions), and navigation hints (navigation.primaryNarrative when mapped).

GET /inventory/issues

Subset of unified inventory — { issues, summary } only.

GET /inventory/graph

Entity relationship graph for the Entities view.

| Query | Default | Description | |-------|---------|-------------| | includeOrphans | true | Include orphan Mongo collections in orphanEntities |

Example:

curl -s "$BASE/inventory/graph"

Returns entities, orphanEntities, memorixDb, discovery, and catalogRelations.


Records (read)

GET /records/collection

Entity collection — descriptor-backed rows, raw Mongo, or auto-detected mode.

| Query | Required | Description | |-------|----------|-------------| | entityName | Yes | Object type name (e.g. assets) | | target | No | entity / event / knowledge | | listId | No | List descriptor id (alias: listDescriptorId) | | contentType | No | Restrict to one content type | | collectionName | No | Explicit collection override | | mode | No | descriptor | raw | auto (default auto) | | + pagination, sort, filter, search | | See HTTP conventions |

Example:

curl -s "$BASE/records/collection?entityName=assets&mode=auto&limit=10&includeTotal=1"

GET /records/raw-collection

Read Mongo documents directly without list descriptor projection.

| Query | Required | |-------|----------| | target | Yes | | collectionName | Yes | | + pagination, sort, search | |

GET /records/full

Full record across content types (multi-collection compose).

| Query | Required | |-------|----------| | entityName | Yes | | One of recordId, entityId, eventId, knowledgeId | Yes — prefer recordId when the object type uses it | | contentTypes | No — comma-separated; default all declared types |

GET /records/item

Item descriptor view — sections and fields for a single record.

| Query | Required | |-------|----------| | entityName | Yes | | One of recordId, entityId, eventId, knowledgeId | Yes — prefer recordId when the object type uses it | | itemDescriptorId | No — default item for entity |

Skills cards (analysis / decisions) — two generic calls

List ids are Catalox metadata, not routes baked into this API. Discover them, then page + open:

# 0) Discover lists for the entity (ids come from Catalox)
curl -sS "$BASE/lists?entityName=assets" | jq '.[] | {id, title, leadingContentType}'

# 1) Page of cards — use the list id returned above
curl -sS "$BASE/lists/$LIST_ID/records?limit=50&includeTotal=1"

# 2) Open card / Explain Decision
curl -sS "$BASE/records/item?entityName=assets&recordId=$ID"

| Platform (stable) | Instance (Catalox lists catalog) | |-------------------|-------------------------------------| | GET /lists?entityName= | Which lists exist, their id / title / leadingContentType | | GET /lists/{listId}/records | Card fields authored on that list descriptor | | GET /records/item | Item sections/fields from memorix-item-descriptors |

Pick Skills lists by leadingContentType of decisions or analysis (and UI title). Do not hardcode list ids in client code unless you intentionally pin to a known Catalox item for one deployment.

Notes for clients

  • Nested decision blobs (e.g. assets data.exploitability → OT decisionExploitability) are usually item fields, not list fields — use /records/item (or /records/full?...&contentTypes=decisions).
  • Prefer /records/item for Skills open; use /records/full only when you need raw multi-CT documents.
  • A given deployment may seed example list ids (e.g. neo); treat those as catalog data. Field maps for the neo instance: .operational/web-client-intergration/02-skills-card-lists.md.

See also docs/consuming-memorix-data.md.

Ad-hoc Assets Exploitability (no Catalox list / item ids)

Same use case without passing Catalox descriptor ids from client code:

# 1) Page decisions
curl -sS "$BASE/records/collection?entityName=assets&contentType=decisions&limit=50&includeTotal=1"

# 2) Open — exploitability = data.exploitability (default item; or use /records/full)
curl -sS "$BASE/records/item?entityName=assets&recordId=$ID"
# curl -sS "$BASE/records/full?entityName=assets&recordId=$ID&contentTypes=snapshots,decisions"

Full field map and paths: docs/consuming-memorix-data.md § Ad-hoc Assets Exploitability.

GET /records/content

Fetch content for a specific item field (e.g. markdown body, external pointer).

| Query | Required | |-------|----------| | entityName | Yes | | fieldPath | Yes (alias: contentKey) | | One of entityId, eventId, knowledgeId | Yes | | itemDescriptorId | No |

GET /records/raw-item

Single Mongo document by id from a named collection.

| Query | Required | |-------|----------| | target | Yes | | collectionName | Yes | | recordId | Yes | | idField | No — override identity field |

GET /records/workspace

Cross-entity workspace list (pinned / multi-entity views).

| Query | Description | |-------|-------------| | workspaceListId | Workspace list descriptor id | | entityName | Filter to one entity | | + pagination, sort, search | |


Snapshots And Associated Data

Normalized snapshot reads expose the client consumption contract from the operational guide. These routes keep storage-compatible names available while presenting associatedInferred as client-facing discovery.

GET /snapshots/{objectType}/{recordId}

Returns a normalized snapshot view:

{
  "objectType": "assets",
  "contentType": "snapshots",
  "recordId": "asset-1",
  "concept": { "...": "..." },
  "data": { "...": "native snapshot data" },
  "analytics": { "...": "optional rollups" },
  "enrichment": { "...": "optional sourced values" },
  "insights": { "...": "optional judgments" },
  "system": { "...": "from _system" },
  "associated": {
    "data": [{ "...": "associatedData[]" }],
    "discovery": [{ "...": "associatedInferred[]" }],
    "analysis": [{ "...": "associatedAnalysis[]" }],
    "custom": {
      "associatedAssets": [{ "...": "custom associated copy" }]
    },
    "properties": []
  },
  "storage": {
    "associatedAliases": {
      "associated.data": "associatedData",
      "associated.discovery": "associatedInferred",
      "associated.analysis": "associatedAnalysis"
    }
  }
}

| Query | Description | |-------|-------------| | include | Comma-separated buckets: data, analytics, enrichment, insights, system, associated | | idField | Override lookup field. Defaults to recordId |

GET /snapshots/{objectType}/{recordId}/associated

Returns only the normalized associated view plus associated-property metadata.

GET /snapshots/{objectType}/{recordId}/associated/{propertyName}

Returns one associated property. propertyName may be a storage name or client alias:

| Client/API name | Storage property | |-----------------|------------------| | data | associatedData | | discovery | associatedInferred | | inferred | associatedInferred | | analysis | associatedAnalysis | | associatedAssets or any associated* | same storage property |

GET /snapshots/{objectType}/associated-properties

Returns managed plus discovered associated-property metadata for snapshot records. Discovery samples the snapshot collection and treats any root-level associated* property as a valid associated property.

| Query | Description | |-------|-------------| | limit / offset | Sample page for discovery | | includeTotal | Include total count from the raw collection read |

Important semantics:

  • Associated arrays are copied read models from linked record data.
  • Legacy properties (associatedData, associatedDiscovery, …) may include inline recordId / contentType.
  • v2 properties (associated<ObjectType><ContentType>) use flat copied data; optional traceability lives in item-level _system.association when enabled. See memorix-pipeline/docs/association-format.md.
  • Refresh resolves snapshot → snapshot first, then fetches non-snapshot content by matched snapshot recordId when applicable.
  • Refresh is one-way: linked content data → source snapshot associated*[].
  • Associated arrays do not include linked record _system, analytics, enrichment, or insights; query the linked collection directly when those buckets are needed.
  • Item-level _system inside associated arrays is stripped from the default snapshot API view; pass include=system to retain it.
  • Root buckets that do not start with associated are still consumable (analytics, enrichment, insights, _system) and can be resolver inputs when configured.

Association refresh (operations)

| Method | Route | Description | |--------|-------|-------------| | POST | /associations/plan | Dry-run association plan with fingerprint | | POST | /associations/apply | Apply plan (requires expectedPlanFingerprint when configured) | | POST | /associations/verify | Verify associated array shapes on source collection |


Lists (read)

GET /lists?entityName={name}

Discover list descriptors registered for an entity.

Response:

{
  "entityName": "assets",
  "lists": [
    {
      "id": "assets-default",
      "title": "Assets",
      "entity": "assets",
      "leadingContentType": "snapshots",
      "fieldCount": 8
    }
  ]
}

GET /lists/{listId}

Full list descriptor document from Catalox.

GET /lists/{listId}/records

Execute the list — same pagination, sort, filter, and search conventions as /records/collection.

GET /lists/suggest?entityName={name}

Suggest extensions and analytics fields for list authoring.

| Query | Description | |-------|-------------| | entityName | Required | | listId | Optional — exclude already-configured extensions/analytics | | mode | automation (default) or ai (requires API key) |

Response shape:

{
  "entityName": "assets",
  "listId": null,
  "mode": "automation",
  "extensions": [
    {
      "kind": "extension",
      "contentType": "analysis",
      "mode": "extendFields",
      "confidence": "high",
      "reason": "Content type \"analysis\" is declared on entity but not joined in this list"
    }
  ],
  "analytics": [
    {
      "kind": "analytics",
      "field": "countAnalysis",
      "collection": "assets-analysis",
      "joinBy": "entityId",
      "metric": { "op": "count" },
      "confidence": "medium",
      "reason": "Count documents in analysis per record"
    }
  ]
}

Lists (write)

All list mutations go through @x12i/memorix-descriptors executeDescriptorMutation. Bodies must match MemorixListDescriptor validation.

| Method | Path | Operation | |--------|------|-----------| | POST | /lists | Create list descriptor → 201 | | PATCH | /lists/{listId} | Update list descriptor | | DELETE | /lists/{listId} | Remove list descriptor | | POST | /lists/{listId}/extensions | Add extension join | | DELETE | /lists/{listId}/extensions/{contentType} | Remove extension | | POST | /lists/{listId}/analytics | Add analytics field | | DELETE | /lists/{listId}/analytics/{field} | Remove analytics field | | PUT | /lists/{listId}/sort | Set compound default sort |

Create list (POST /lists): pass a full list descriptor object (must include id, entity, leadingContentType, fields, etc.). See seed examples under memorix-descriptors/catalox-seeds/inputs/list-descriptors/.

Set sort (PUT /lists/{listId}/sort):

{
  "sort": [
    { "property": "severity", "direction": "desc" },
    { "property": "entityId", "direction": "asc" }
  ]
}

Mutation failures return 400 with { "error": "...", "result": { ... } }.


Record writes

POST /records/write

Write Memorix payload documents via a Catalox write descriptor.

Single record:

{
  "writeDescriptorId": "asset-analysis-write",
  "entityId": "10.150.68.31",
  "operation": "upsert",
  "input": { "riskLevel": "HIGH", "summary": "..." },
  "metadata": { "source": { "uri": "file://scan.json" } },
  "dryRun": false,
  "validateOnly": false
}

Batch:

{
  "writeDescriptorId": "asset-analysis-write",
  "operation": "upsert",
  "records": [
    { "entityId": "a", "input": { "...": "..." } },
    { "entityId": "b", "input": { "...": "..." } }
  ],
  "continueOnError": true,
  "dryRun": false
}

| Field | Description | |-------|-------------| | writeDescriptorId | Required — Catalox write descriptor id | | operation | add, upsert, patch, or replace | | entityId / eventId / knowledgeId | Exactly one identity for single writes | | input | Payload object (single write) | | records | Array of record inputs (batch) | | dryRun | Plan without writing | | validateOnly | Validate input only | | continueOnError | Batch — continue after per-record failure |

Returns 200 on success, 422 when validation/write fails. See @x12i/memorix-writer for descriptor shapes and content uploads.


Narratives

The authored catalog lives on entity descriptors at descriptor.narratives (validated MemorixNarrativeDefinition entries). Per-record tags live at doc.narratives.{key} (written by pipeline narrative-sync or Explorer assignment).

Merged list/detail endpoints combine authored catalog + platform-generated signals + optional live Mongo counts. Use the raw endpoint when you need only the stored descriptor map (e.g. Exellix catalog consumption).

GET /narratives

Global narrative catalog (merged).

| Query | Description | |-------|-------------| | entity | Filter to one entity | | target | Filter by target | | agentId | Filter by agent scope | | includeLiveCounts | Attach live record counts |

GET /narratives/{entity}

Narratives for one entity (merged).

GET /narratives/{entity}/raw

Authored descriptor map only — Record<slug, MemorixNarrativeDefinition> as stored on the entity descriptor (no signal discovery merge).

GET /narratives/{entity}/{key}

Single narrative (merged enriched summary).

POST /narratives

Create an authored narrative entry on a descriptor. Requires metadata writes.

Catalog shape (kind required):

{
  "entity": "assets",
  "key": "has-vulnerabilities",
  "label": "Has vulnerabilities",
  "kind": "having-signal",
  "sourceRef": "assetVulnerabilities",
  "targetEntity": "vulnerabilities"
}

Implementation shape (legacy condition-based narratives): label, detectionMethod, condition.

PATCH /narratives/{entity}/{key}

Update an authored entry. Platform-generated keys return 409.

DELETE /narratives/{entity}/{key}

Remove from descriptor. Optional ?removeTags=1 clears record tags.

GET /narratives/{entity}/{key}/records

Paginated records tagged with the narrative.

curl -s "$BASE/narratives/assets/high-risk/records?limit=20&includeTotal=1"

Filter any list by narrativeId

Narrative tags sit on snapshots (doc.narratives.{key}), but the same key filters lists and other content types via the virtual filter property narrativeId (alias narrativeKey):

# Default list / snapshots
curl -s "$BASE/lists/vulnerabilities-main-list/records?filter=narrativeId:eq:is-vulnerability"

# Analysis (or discovery/decisions) linked to tagged snapshots
curl -s "$BASE/records/collection?entityName=vulnerabilities&contentType=analysis&filter=narrativeId:eq:is-vulnerability&mode=raw"

# User-facing filter dropdown options
curl -s "$BASE/narratives/vulnerabilities/filter-options"

# Execute a dashboard listQuery object
curl -s -X POST "$BASE/records/query" -H 'Content-Type: application/json' \
  -d '{"entityName":"vulnerabilities","listDescriptorId":"vulnerabilities-main-list","filters":[{"property":"narrativeId","operator":"eq","value":"is-vulnerability"}]}'

eq / in = has tag; ne / nin = missing tag. See Consuming Memorix data — Narrative filter.


Object types & root property catalog

Read-only object-type summaries from Catalox descriptors, plus computed root-envelope property stats.

GET /object-types

All object-type summaries (properties, declarative rootProperties, authored narratives, rootPropertyCatalog when present).

GET /object-types/{name}

Single object-type summary.

GET /object-types/{name}/root-property-catalog

Computed rootPropertyCatalog array. Optional ?contentType=snapshots filters to one content type.

POST /object-types/{name}/root-property-catalog/compute

Full-scan Mongo collections and persist rootPropertyCatalog on the descriptor. Requires metadata writes.

{ "ttlMs": 86400000, "contentTypes": ["snapshots"], "force": false }

Response includes skipped / reason: "fresh" when TTL not expired and force is false.


Agents

GET /agents

Returns the agent registry from inventory policy (array of agent objects). Used by Explorer for agent-scoped narrative filtering.

curl -s "$BASE/agents"

Route summary

| Method | Path | Description | |--------|------|-------------| | GET | /health | Memorix + Mongo + Catalox health | | GET | /inventory/collections, /inventory/summary | Unified inventory | | GET | /inventory/issues | Inventory issues only | | GET | /inventory/graph | Entity graph | | GET | /records/collection | Entity collection (descriptor/raw/auto) | | GET | /records/raw-collection | Raw Mongo collection page | | GET | /records/full | Multi-content-type full record | | GET | /records/item | Item descriptor view | | GET | /records/content | Single field content | | GET | /records/raw-item | Raw Mongo document by id | | GET | /records/workspace | Workspace list | | GET | /snapshots/{objectType}/{recordId} | Normalized snapshot view | | GET | /snapshots/{objectType}/{recordId}/associated | Normalized associated arrays | | GET | /snapshots/{objectType}/{recordId}/associated/{propertyName} | One associated property by alias or storage name | | GET | /snapshots/{objectType}/associated-properties | Managed/discovered associated-property metadata | | POST | /records/write | Write record(s) | | GET | /lists?entityName= | List discovery | | GET | /lists/suggest?entityName= | Extension/analytics suggestions | | GET | /lists/{listId} | List descriptor | | GET | /lists/{listId}/records | List rows | | POST | /lists | Create list | | PATCH | /lists/{listId} | Update list | | DELETE | /lists/{listId} | Delete list | | POST | /lists/{listId}/extensions | Add extension | | DELETE | /lists/{listId}/extensions/{contentType} | Remove extension | | POST | /lists/{listId}/analytics | Add analytics | | DELETE | /lists/{listId}/analytics/{field} | Remove analytics | | PUT | /lists/{listId}/sort | Set default sort | | GET | /object-types | Object-type descriptor summaries | | GET | /object-types/{name} | One object-type summary | | GET | /object-types/{name}/root-property-catalog | Computed root envelope property catalog | | POST | /object-types/{name}/root-property-catalog/compute | Recompute and persist root property catalog | | GET | /narratives | Narrative catalog (merged) | | GET | /narratives/{entity} | Entity narratives (merged) | | GET | /narratives/{entity}/raw | Authored narratives map from descriptor | | GET | /narratives/{entity}/{key} | One narrative | | POST | /narratives | Create authored narrative | | PATCH | /narratives/{entity}/{key} | Update authored narrative | | DELETE | /narratives/{entity}/{key} | Delete authored narrative | | GET | /narratives/{entity}/{key}/records | Tagged records | | GET | /agents | Agent registry |

Also: GET /health at server root (process liveness only).


Commands

| Script | Purpose | |--------|---------| | npm run dev | Start API server (port 5181) with tsx | | npm run build | Compile TypeScript to dist/ | | npm run serve | Production server (node dist/cli.js serve) | | npm test | Live API smoke tests (server must be running) | | npm run test:live | Same as npm test — runs all smoke scripts | | npm run smoke:api | Core health + inventory + narratives smoke | | npm run smoke:inventory | Inventory lenses + raw/descriptor record drill | | npm run smoke:records | Workspace + item fetch | | npm run smoke:snapshots | Normalized snapshot + associated data reads | | npm run smoke:narratives | Narrative catalog + records | | npm run smoke:entities | Graph → collection navigation spine | | npm run mongo:inventory | CLI inventory report (no HTTP server) | | npm run catalox:seed:memorix-retrieval:apply | Apply Catalox retrieval seeds | | npm run catalox:seed:memorix-retrieval:validate | Validate seeds without applying |

Live test workflow:

# Terminal 1
npm run dev

# Terminal 2
npm test

Catalox seeds

Before first use in a new environment, apply retrieval seeds so entity/list/item descriptors exist:

npm run catalox:seed:memorix-retrieval:apply

Requires MONGO_URI and CATALOX_APP_ID (default memorix). Validates with:

npm run catalox:seed:memorix-retrieval:validate

Migration: lists catalog rename

If upgrading from a deployment that used the legacy lists catalog id, run once per Catalox app:

cd ../memorix-descriptors
CATALOX_APP_ID=memorix node scripts/migrate-list-descriptors-catalog.mjs [--dry-run]

Related documentation

| Document | Topic | |----------|-------| | docs/consuming-memorix-data.md | Read-only consumption guide | | docs/advanced-apis.md | Writes, pipeline, associations, metadata | | memorix-pipeline docs | Enrichment CLI and association format | | memorix-retrieval README | Underlying read APIs, graph runs, inventory lenses | | memorix-descriptors README | List descriptor shapes and mutations | | memorix-writer README | Write descriptors and record operations | | memorix-explorer integration | How the UI consumes these routes | | MEMORIX-CATALOX-CONTRACTS | Catalog ids and descriptor JSON formats | | MEMORIX-DATABASE-CONVENTIONS | Mongo layout and env resolution |


Package exports

import {
  createMemorixExplorerApp,
  startMemorixExplorerHttp,
  EXPLORER_API_PREFIX,
} from "@x12i/memorix-explorer-api";

Use these to embed the Explorer API in another Node host or serve the UI from the same process. For custom gateways, proxy HTTP to /api/explorer/* rather than importing internal handlers.