@corbits/memory is a memory add / search / list SDK that mounts onto a host
Interchange app. It owns the memory Postgres schema (pgvector), capture,
hybrid search, retention and the optional distiller helpers. It does not own
authentication, grants, tenancy, or model inference: embedding and reranking
are outbound HTTP calls to configured endpoints. There is no server, port or
process entrypoint here, and there never should be.
src/index.ts— public surface:createMemory,createMemoryRoutessrc/mount-config.ts/src/config.ts— mount config + engine configsrc/routes/— Hono routes (add,search,list,feed, retentionforget/purge/retention-class)src/workflow-mount.ts— run-scoped routes for deployed agents (agent-token bearer + run address; no session auth)src/sidecar-bundle.ts+src/tools.ts— the agent tool bundle (@corbits/memory/sidecar-bundle) and its tool descriptorssrc/http-client.ts— host-side HTTP client for the tenant routessrc/services/— capture / search / transform internals (not public verbs)src/ports/—DocumentStore/SourceProvider+ fakessrc/core/— embed/rerank clients, merge, arktype schemassrc/db/+migrations/— Drizzle schema + SQL migrations (pgvector,memory.*)src/distiller/— optional process helpers (@corbits/memory/distiller)docs/— how access control, temporal ranking, relevancy, retention and the feed work; referenced from the codee2e/— real-Postgres suites
-
Authenticate nothing. Identity defaults to
c.get("principal")from the Interchange context; a host may instead supplycallerResolver(src/routes/deps.ts) to resolve a non-browser caller (e.g. a workflow-run child's own sidecar bearer token) — but resolving that token is 100% host logic, called through the seam, never implemented here. Either way authorization goes through the host's grant store (@intx/authz) via the samerequireGrantpath. Never add API keys, sessions, or OAuth here.ResolvedCaller(thecallerResolverreturn type) is frozen at exactly{ tenantId, principalId }. It carries no roles, no grants, no authorization hints of any kind — it is a shape conversion (host identity in, context principal/tenant out), never an authorization decision. A resolved caller traverses the identicalrequireGrant/grantGuardpath a browser caller does and can never bypass it. Before widening this type — "let it carry roles too," "let a trusted caller skipgrantGuard" — stop: either change turns the conversion shim into the library making an authorization decision, which IS the invariant this rule exists to name. If a host needs richer machine-caller authorization, that logic belongs in the host's own grant store /callerResolverclosure, resolved down to{ tenantId, principalId }before it ever reaches this package — not in a widerResolvedCaller. -
One Postgres:
DATABASE_URL, the engine's own vector plane, under thememoryschema — never the host's control-plane DB. No foreign keys into control-plane tables; cross-refs (tenant_id,principal_id) are plaintext. -
Never embed in-process. Embedding/reranking are outbound HTTP calls to configured endpoints. A model endpoint is a trusted URL, same as the database URL — no self-host flags, no SSRF filtering here.
-
Validate at the edges with arktype — route bodies and model-endpoint responses. Keep the version range compatible with Interchange's catalog (currently
^2.1.29).
bun install && bun run check- Read
README.mdbefore changing public behavior. - Keep changes focused; update docs and tests alongside code changes.
- Run
bun run checkbefore declaring work complete.