RememberStackremember.dev/docs

Project Status

Target release coordinate: v0.17.0 on PyPI and GHCR (release coordinate is remember==0.17.0 with terminal transition package rememberstack==0.17.0).

What ships today

Core memory engine

  • Full evidence spine: ingest → convert → structure → chunk → claim extract → entity resolve → fact adjudicate
  • Immutable claims with occurrence-level multi-span source grounding, adjudicated relations and observations, bi-temporal validity, supersession, contradiction groups
  • Frozen Selection results share bounded, source-backed references with later extraction; version reuse covers all cited ranges and earlier context inputs
  • Testimony currency and lineage-based evidence counting when documents change or extractors re-run
  • Watched sources and versioned document lineages (including a local directory watcher)

Facts use one mutable chosen world-time window with explicit precision. Contextual adjudication handles identity and date corrections together; immutable source claims remain available as evidence. Processing prompts treat a shared person or event as distinct from the proposition being made, and the adjudicator reads compact semantic evidence rather than database bookkeeping. Retrieval distinguishes confirmed temporal matches from incomplete-date candidates. Entity descriptions and fact labels show dated history. Stores created before chosen world dates are recreated, not converted; see deployment.

Retrieval and agent surfaces

  • PostgreSQL-native search indexes, a live SQL/PGQ graph, and a browsable corpus filesystem
  • Four assured operations: resolve_entity, claims_and_sources_context, facts_context, combined_context
  • ID-scoped facts_context / combined_context use bounded live graph neighborhoods for current and at; unavailable graph authority is an explicit boundary, while history and overlap remain visibly anchor-scoped
  • Open query: sandboxed SQL, typed live-graph helpers, saved examples, schema discovery
  • MCP assured-operation and open-query tools (stdio client transport)
  • Self-accounting response envelopes (grain, freshness, contradictions, typed negatives)
  • API, CLI, and MCP in lockstep; optional filesystem mounts and a deployment-rendered consumption skill
  • An OpenAPI document for the query API a self-host deployment serves, kept in the repository and compared against the code in CI, so a client can be generated rather than transcribed. The liveness probe stays out of it deliberately. A deployment does not serve the document either — the schema route sits outside the auth perimeter — and releases from here on attach it as an asset
  • Cold-agent skill checks so harnesses keep claims and facts straight

Knowledge pages

  • Compiled and authored markdown knowledge in git
  • Mechanical routing and staleness; authored principles are never machine-rewritten

Operations

  • Docker Compose single-deployment self-host (PostgreSQL 19 prerelease, object storage, API, continuous workers)
  • Optional API Bearer BIND (REMEMBERSTACK_SELFHOST_API_BEARER_BIND); default Compose stays open
  • Optional signed-credential perimeter (REMEMBERSTACK_SELFHOST_API_SIGNING_KEYS, a JWKS of Ed25519 public keys): verifies EdDSA credentials issued elsewhere, so more than one caller can reach a deployment without pushing a secret per caller. Composes with the Bearer BIND when both are set; unset changes nothing
  • PostgreSQL-native P1 search indexes; no separate search-store maintenance process
  • Work ledger with retries, dead-letter queue, optional spend ceilings
  • Request-path surface metering plus a content-free v_cost_receipts export (remember ops cost-export and optional second-bind HTTP)
  • Optional managed-hosting ingest metering under the Compose managed profile: exact normalized document-text receipts, durable replay and outcome handling, and hard-forget cancellation. Default self-host Compose does not start it.
  • remember login / logout via a token-host device grant (CLI credential file only)
  • Machine-verifiable readiness; projection rebuilds; hard-forget; portable restore order
  • Optional observability exporters (off until configured)

Packaging

  • Single canonical remember wheel on PyPI providing the unified platform CLI (remember) and Python SDK (from remember import RememberClient, MemoryClient, with 100% backward-compatible Client and CloudClient interfaces)
  • Container-first engine distribution via ghcr.io/writeitai/remember-stack and Docker Compose
  • One semantic version across PyPI, GHCR image, and Compose

Smoke-profile limits (be honest)

AreaStatus in default Compose smoke
Markdown ingestSupported
PDF / HTML conversionEngine-capable (mistral_ocr, markitdown); register routes — not auto-wired in the smoke profile
Static image conversionEngine-capable dual-lane route image_ocr_description (dedicated OCR + independent vision-LLM description for static image/png and image/jpeg); register routes and both provider keys — not auto-wired in the smoke profile. Video/audio conversion is not shipped
Query-time entity resolveExact-name match; richer matching runs at write time
Competitive benchmark scoresOperator-run protocols; not a continuous public scoreboard

What landed after v0.3.0 (in v0.4.0)

  • MCP memory write verbs ingest + pipeline_readiness shipped in v0.3.0.
  • v0.4.0 adds request-path surface metering, rememberstack.cost_export.v1 (CLI + optional second-bind HTTP), and remember login / logout.

What landed after v0.4.0 (in v0.4.1)

  • Remote MCP tools/list treats GET /operations 404 as “registry not mounted” and still advertises ingest + pipeline_readiness (managed UMC token surface). Auth and transport failures still fail the list.

What landed after v0.4.1 (in v0.4.2)

  • Stock self-host convert routes text/plain through the same UTF-8 passthrough as text/markdown (#302). CLI/SDK .txt ingest no longer dead-letters as unroutable MIME.

What landed after v0.4.2 (in v0.4.3)

  • Optional API perimeter: SHA-256 Bearer BIND {issued_deployment_uuid}:{sha256hex} (#305). Unset keeps the open quickstart. GET /healthz stays unauthenticated.

What landed after v0.4.3 (in v0.5.0)

  • PostgreSQL 19 SQL/PGQ now serves fixed one-hop live graph patterns; bounded recursive SQL serves deeper neighborhood, shortest-path, and citation traversal with explicit expansion, frontier, result, time, memory, and connection limits.
  • PostgreSQL is the single graph authority. The legacy embedded graph store, public Cypher, graph-build workers, and snapshot serving were removed in this pre-1.0 clean cut.
  • Typed graph HTTP and Python SDK operations expose current and bi-temporal as-of traversal. The release also publishes a multi-architecture PostgreSQL 19 Beta 3 foundation with pinned pgvector, pg_textsearch, and pg_partman.

What landed after v0.5.0 (in v0.6.0)

  • facts_context now composes lexical/vector search, referent resolution, bounded live-graph expansion, and evidence hydration under one snapshot and deadline. Same-name exact matches remain visible candidates rather than a silent entity choice.
  • The assured-operation registry publishes the new retrieval contract as operation version 2, and the bundled saved query follows the same default.
  • Graph planner settings are deployment-local and transaction-contained, with migrations and readiness checks for the PostgreSQL 19 execution plans.

Unreleased on main

  • Stored originals remain usable without a converter (D117). Otherwise admissible uploads retain their raw bytes. Conversion starts parked with defer_reason=no_route; missing routing does not mark the version failed. A worker with stale configuration also parks without consuming an attempt allowance.
  • Raw availability and processed currency are separate. After P3 rebuild and mount publication, the stub's stored_raw_uri points to the latest durable original. A separate processed-version section retains any older working summary and full text. Configure real provider raw/artifact mount roots as described in Mounts; default empty directories do not serve bytes.
  • Resume only supported backlog. Run remember ops resume-no-route --deployment <uuid> with the new route configuration after restarting workers. Only now-routable live versions are released. Previously dead-lettered versions still require remember ops replay.
  • Breaking for library consumers: UploadIngestor requires routable_mimes=frozenset(conversion_routes). The catalog checks the canonical stored MIME, including when the same bytes return with a different declaration.
  • Managed text admission and classification are unchanged. Unaccepted staged originals are omitted from P3. After approval, a worker missing the route parks conversion; this does not introduce cloud binary admission or storage-only billing.

What landed after v0.9.0 (in v0.10.0)

  • A shared name no longer costs a candidate anything. The generic-identifier guard flagged any name linking two or more entities and used that flag as the primary blocking sort key — ahead of match score. A near-exact hit on a common name could rank below a barely-matching unusual one and be truncated out of the candidate list entirely.
  • The premise was wrong: it counted entity rows, not people. Because T0 never auto-merges, the resolver deliberately mints a second row for one real person pending adjudication — and the guard read its own conservatism as proof the name was generic. Ten unrelated Jan Nováks and one Jan Novák recorded twice were indistinguishable to it, and it demoted both.
  • Fuzzy blocking now ranks by match score, then by how closely the entity's own canonical name resembles the query, then by age, then by id. That replaces a tiebreak that fell through to a random UUID whenever candidates matched through the same name.
  • D21's promiscuous-signal concern ([email protected], placeholders) moves to the mechanisms that decide identity — T3 profile evidence, T4, and cannot-link exclusion edges — rather than demoting candidates before anything examines them.
  • What this costs, stated plainly: an overflowing fuzzy block no longer truncates promiscuous-string matches first. The candidate set still reports honestly when it was truncated. On a deliberately degenerate shape (100k entities sharing one fuzzy name) the ranking query is measurably slower — runs ranged +3.3% to +31%, consistent in direction, not in magnitude.
  • Operators: migration p9_23_0044 drops generic_identifier_guard. Because that table carries a foreign key to deployments, the drop briefly takes an AccessExclusiveLock on the tenancy root, which blocks readers as well as writers; it is scan-free and short, and the migration sets a 5s lock_timeout so it fails fast rather than queueing behind a long read. The resolver generation moves to resolver-2026.08g, so evaluation curves measured under 08f are not comparable and a fresh run is required.

What landed after v0.15.0 (in v0.16.0)

Dated events no longer collapse merely because their words match. The observation adjudicator now uses each claim's resolved world-time window. Events with disjoint dates stay distinct unless a date-aware verdict identifies one occurrence with disputed dates; a dated event also cannot serve as evidence for an undated summary. Recurring same-shaped events therefore survive as separate facts, and ingestion provenance changes (#360).

Claim windows have one canonical half-open meaning. Day, month, quarter, and year precision cover their complete calendar unit; instants remain non-empty points; open and unknown bounds stay explicit. claims_as_of now finds a day-precision claim from an intraday or point query, and observation adjudication uses the same bounds (#372).

Operators: migration p9_26_0047 adds the canonical-bound SQL functions and the partial expression index used by claims_as_of; claim storage is unchanged. Observation generations roll, so rebuilding changes stored ingestion provenance, and claims_as_of results change under the corrected window semantics. Migration p9_27_0048 publishes those bounds to open SQL as memory_v1.canonical_bounds and claims_canonical; the shipped examples.claims_as_of query overlaps the canonical window and counts unknown-precision claims by precision.

A release now carries the API contract beside the binaries. The generated openapi.json is attached to the GitHub release, so client generation can pin the exact schema served by the installed engine rather than following main (#358).

Browser uploads have their own narrow credential. The signed perimeter now has read, ingest, and write scopes. ingest reaches only POST /ingest: it cannot search, list memory, run operations, or create a connector that keeps pulling after the browser leaves. Full write still reaches ingest. A narrow credential also cannot assert immutable principal attribution (#363, #370).

Managed login uses the deployment hostname it receives. When the token host advertises a live data-plane hostname, remember login derives the query API URL, so managed users need only --token-host. An explicit --api-url still wins for self-hosted and local deployments. A missing hostname asks for that flag, while a hostname that is not live prints the name and asks the user to retry; neither refusal replaces an existing credential (#371).

What landed after v0.14.0 (in v0.15.0)

A deployment can tell you what it holds. GET /documents lists document lineages — title, source, when each was ingested, the newest version's processing state, and the engine's own error when one failed. It is what lets an app show somebody which file arrived rather than only how many.

The ordering is first_seen_at and never moves. "Most recently touched" is the tempting order and the wrong one: re-ingesting a document would raise it and tombstoning a version would lower it, and a keyset cursor against a moving key silently drops rows a reader never sees and repeats ones they already have.

Search without putting the query in the URL. POST /search/claims and POST /search/chunks take the terms in a body. A query is usually the most sensitive string in the exchange, and a URL is written to access logs, kept by proxies and retained in browser history. The GET forms are unchanged for clients on a private path.

A deployment can name the browser origins allowed to call it. REMEMBERSTACK_SELFHOST_BROWSER_ORIGINS, empty by default. Without it a browser refuses every cross-origin request before the credential is even examined, and the failure looks like the deployment being down.

What landed after v0.13.0 (in v0.14.0)

  • remember login works again. It has failed since 2026-08-25 with a validation error: the managed token host began returning two fields the client's response model refused. 0.13.0 does not contain this fix — it was released from main while the change was still on its branch, so an 0.13.0 install is as broken as an 0.12.0 one. That model now ignores fields it does not know, the way a client of a separately deployed service has to (#351).
  • Login mints before it revokes. Replacing a credential no longer starts by destroying the one you have, so a login interrupted at the browser step leaves the existing credential working. The credential being replaced is journalled until the token host confirms it is gone, and any command will tell you if one is still outstanding.
  • A credential can carry an expiry. login prints it, it is stored, and every command warns on stderr within 30 days of it — so a scheduled job does not fail at 3am with an unexplained 401. No expiry recorded means none was sent, not that the credential never expires.
  • The signed-credential perimeter is stricter. Exactly one audience, and it must be this deployment; nbf, scope and a non-empty credential id are required; the key set must be Ed25519 public keys and is refused if any declared key fails to load. Nothing here changes an open quickstart or a deployment using only the Bearer BIND.

What landed after v0.8.1 (in v0.9.0)

  • Ingest records who created a version as a typed principal (user | api_credential | service). A machine credential is never collapsed into the person who minted it; attributing token activity to that person would be false attribution.
  • Attribution is creation-scoped and immutable. Bytes identical to the lineage's latest version remain the D55 no-op, so a later submitter of the same bytes never rewrites who created it.
  • The pair travels in X-Ingest-Principal-Kind / -Ref headers, never the query string: the reference is erasable PII and a URL is copied verbatim into access logs, proxies and traces.
  • It is honoured only where the deployment declares REMEMBERSTACK_SELFHOST_TRUSTED_PRINCIPAL_SOURCE (default off, for a control-plane-only perimeter) and, when an API perimeter is configured, the caller has full write authority. Elsewhere attribution is ignored, never rejected — metadata must not be able to fail an otherwise valid ingest.
  • Deleting a principal nulls attribution and never destroys the document version. This is row deletion, not a D74-grade hard forget; a person-grain forget target is a later slice.

What landed after v0.8.0 (in v0.8.1)

  • Write-time T4 makes one binary, match-biased simple-model call over the complete bounded candidate snapshot. It selects a supplied entity id or new; confidence is audit evidence and never routes to a frontier model.
  • The T4 prompt includes ordered candidates, aliases, current profile descriptions, salient facts, and T3 scores/gates. Missing overlap or a different topic is not evidence for a new entity.
  • RS-LoCoMo-Full-v17 fingerprints this resolver generation while retaining v16's read surface, answer/judge seats, budgets, and content-before-Unknown guard.

Document-local exact T0 (current release behavior)

  • Resolver resolver-2026.08h can replay an exact canonical-name match at T0 (a behavior introduced in 08f) only inside the same document and only after a T4 match established one unambiguous live binding. Fuzzy T1/T2 signals remain candidate-only.
  • The derived binding projection is generation-gated as document-t0-v1, rebuilt before it is enabled for existing deployments, and cleared by normal lineage deletion and hard forget.
  • Historical RS-LoCoMo-Full-v18 pins the binding generation in addition to the v17 retrieval, answering, judging, budget, and answer-attempt contracts.
  • Historical RS-LoCoMo-Full-v19 changed only the frozen answer prompt: a hypothetical may follow causal or motivational relationships in retrieved evidence even when no source states the hypothetical verbatim. It adds no retrieval, retry, reasoning-effort, or call-budget work.
  • Historical RS-LoCoMo-Full-v20 also requires every distinct retrieved value that directly satisfies the question. It does not add a retrieval or retry.
  • The current RS-LoCoMo-Full-v24 pins D107 WP-T.4: all four claim temporal kinds, open-ended state windows, and full source timestamp headers. The extractor generation rolls; the fact schema and query-space contract do not.
  • Historical RS-LoCoMo-Full-v23 pins the query-space publication of those bounds (D107 WP-T.0b): memory_v1.canonical_bounds, claims_canonical, and the rewritten examples.claims_as_of. Ingestion and the library claims_as_of path are unchanged from v22; the surface manifest hash rolls.
  • Historical RS-LoCoMo-Full-v22 pins canonical half-open claim bounds (D107 WP-T.0a): a day-precision claim is found from an intraday claims_as_of window and the observation adjudicator no longer treats a day as a zero-width point. Ingestion provenance and claims_as_of results change; the benchmark's tool set does not.
  • Historical RS-LoCoMo-Full-v21 pins the D106 observation adjudicator: dated events with disjoint resolved windows never collapse onto or supersede each other (a same-occurrence date dispute may contradict), and a dated event is never evidence for an undated statement. Recurring same-shaped events (seven tournament wins months apart) now survive as distinct facts. It changes ingestion provenance only; retrieval, prompts, budgets, and scoring are those of v20.

Mutable temporal facts

Facts have one chosen world-time window. Ordinary adjudication uses source dates and surrounding context to identify repeated events and revise a fact's dates. Claims retain the source's original testimony. Historical retrieval and dated summaries use the chosen window without a stored state/event taxonomy or separate verdict dates. The evaluator generation is Full-v36; this change does not establish a benchmark score improvement. Full-v36 requires the ground_claims ingest stage, pins D122 source-backed references plus D123 contextual nomination, uses structure generation e0-structure-2026.07g:d79-wave2 so the fallback nested JSON field is subsections, pins normalizer generation …:both-lists-1:t4-format-1:nested-fields-1 so both observations and relations arrays are required, nested observation / relation / EntityRef fields are named, and T4 names all four existing fields, pins resolver resolver-2026.08h, and pins fact-application generations …:output-fields-1:new-fact-refs-1 so all nine existing fact-decision fields are named, window=null when no explicit date replacement is intended, and a new fact is declared as N1 rather than an invented F-name. Dataset, models, retrieval, budgets, and scoring are unchanged.

Stores created before this change are recreated and re-ingested; the migration refuses a populated database rather than converting it (deployment).

What landed after v0.6.0 (in v0.7.0; historical resolver generation)

  • Entity adjudication is tri-state. Thin evidence and bounded-prefix truncation mint explicit provisional fragments without poisoning later convergence; only positively supported difference creates an effective cannot-link.
  • T3/T4 provider calls no longer hold the normalized-lemma advisory-lock transaction. Candidate/profile authority is revalidated before commit with bounded retry on contention.
  • Successful profile publication invokes bounded local convergence. Automatic merge remains fail-closed by default, and equivalent review proposals are deterministic and deduplicated.
  • RS-LoCoMo-Full-v16 retained v15's read surface and budgets while enforcing a content-bearing attempt before terminal Unknown after identity-only reads.

Intentionally not in the library

  • Multi-tenant hosted control plane, billing, or dashboards
  • Production SLA claims from these docs
  • Automatic free-model routing for publication benchmarks
  • Secret management product (use your own secret store)

Docs map

Following along