Repository navigation
Releases: Hilbras/Remembra
Release list
Remembra 5.7.1 — retrieval, SDK and cold-start fixes
Three fixes, all found by installing 5.7.0 from the registry and using it, plus two
cold-start races found by building a reproduction rather than re-running a suite.
Full compatibility statement: docs/v5.7.0-compatibility.md.
Findings and measurements: docs/v5.7.0-audit.md.
Fixed
-
Roadmap §37's budget is reachable through the typed SDK.
SearchInput— which
the SDK'sSearchOptionsis an alias of — never gainedbudget, so a typed caller
could not set a bound. Adding it exposed a second half: the SDK serialises query
parameters withString(value), so a nested object became the literal string
"[object Object]"and the server ignored it. The SDK accepted the argument, the
type allowed it, and the bound was never applied. Nested parameters are now
flattened asparent.child. The response side had the same gap in reverse: the
server has returnedbudgetsince §37 shipped andSearchResponsedid not declare
it. -
Non-ASCII queries now reach the lexical path.
extractQueryfiltered query
terms with/^[a-z0-9]+$/u, so every non-ASCII query produced zero terms, no
keyword list was built, and retrieval fell back to the vector path alone — nothing at
all without an embedding provider. A CJK, Russian, Greek, Hangul or Thai query
returned an empty result set with no error and no warning, which is
indistinguishable from a query with no match. Both halves of retrieval now share one
tokeniser, so they cannot drift apart again.One ASCII behaviour changes, pinned by a test: a query with internal punctuation now
contributes its alphanumeric parts, sodon'tcontributesdonand matches a
document containingdon't. -
latest N <query>now parses.TEMPORAL_REanchored its alternation with$,
so each branch had to consume the entire query —latest 3 errorsfell through and
was tokenised as the ordinary words latest and errors. Both branches now carry a
trailing remainder, with the digit count still required solatest news …stays an
ordinary query.This restores parsing, not meaning. The qualifier's values are read in exactly
one place, where they switch the recency multiplier from 0.5 to 2.latestCount
still does not limit to N, andbefore/afterstill filter nothing. Measured, with
four documents of decreasing age and all four matching:search(pool, "incident review") -> d1, d2, d3, d4 search(pool, "latest 2") -> d1, d2, d3, d4 search(pool, "before 2026-08-01") -> d1, d2, d3, d4 -
Two cold-start races in the batch idempotency ledger. Found by building a
reproduction, because the symptom was a single MATRIX-02 failure that passed on every
rerun.- A peer's integrity-key staging file made the next start fail.
link()needs its
source to exist, so publishing the key necessarily has a window in which a
.claims.key.<pid>.<hex>.tmpis visible in the claim directory, and the
constructor's allowlist rejected that name. Only a staging file this code creates
is now tolerated, and the constructor waits for its publisher. Anything else is
still refused immediately. - The ledger identity was published with
open(O_EXCL)followed by a write, so
the file existed empty before being filled; a peer reading in between refused to
start with "ledger identity is invalid". Same defect as the zero-byte integrity key
fixed in 5.6.0, which survived because that fix was applied toclaims.keyand not
toclaims.identity. Now published withlink()too.
Measured over 60 rounds of 16 concurrent cold starts: "unrelated files" goes from
routine (hundreds of failures) to zero; "ledger identity is invalid" from ~1 in 300 to
zero.Two rarer symptoms in the same family remain and are not fixed — they are in code
this release does not touch, and a per-symptom wait is what produced five
manifestations in the first place: "ledger database exists without its identity" and
"database could not be opened" (SQLite contention), about 3 occurrences across
~250,000 cold starts. This store has now produced six manifestations; the real fix is
a single atomic initialisation, and this release does not pretend six patches reach it. - A peer's integrity-key staging file made the next start fail.
Known limitations
- The temporal qualifier parses but does almost nothing beyond the recency multiplier.
See audit S8. - The benchmark set contains no temporal query and no non-ASCII document, so the gate
cannot see two of the fixes above. The unit tests cover them. Both are gaps in the
gate, recorded rather than papered over. - Two cold-start symptoms listed above remain in the ledger.
Gates
940 test suite · 142 security matrix · 52 recovery matrix · Python SDK ·
47 documentation files · 23 benchmark scenarios · 0 audit findings · 0 npm
vulnerabilities. Full release:check passed for @hilbras/remembra@5.7.1 on
Node 18.20.8.
Remembra 5.7.0 — Advanced Retrieval Engine (§31–§38)
Roadmap §31–§38 — the Advanced Retrieval Engine milestone.
Full compatibility statement: docs/v5.7.0-compatibility.md.
Benchmark gate: docs/benchmark-gate.md. Audit: docs/v5.7.0-audit.md.
Two default-on behaviour changes
Everything else here is additive with unchanged defaults. These two are not.
- Exact duplicate results are collapsed. Two records of the same statement are
one answer to a question. Escape hatch:dedupeExact: false.
Same-source collapsing is off by default (dedupeSameSource) — identical text
from two different sources is often corroboration, and discarding the second
source is a worse error than showing a repeat. - Superseded memories are excluded. A query for the current policy no longer
returns v1 and v2 side by side. Escape hatch:includeSuperseded: true.
Suppression applies only when the replacement is actually available, so a
danglingsupersededBykeeps the old copy visible rather than replacing it with
nothing.
Neither deletes anything. The store still holds every version, each readable by id.
New in this release
- Token efficiency and duplicate rate in the evaluation harness — the two of
§38's seven metrics that were missing, and the guardrail the deduplication work
needed. - Whole-token matching, phrase scoring and field weights. A query for
cat
scored"concatenate the streams"at exactly the same 60.00 as"the cat sat",
because matching was a singleString.includes. - Real inverse document frequency. The old "idf" was computed from the document
being scored — a coverage discount wearing the name of IDF, which cannot tell a
rare term from a common one. - One retrieval budget object covering all four §37 dimensions. Exceeding
maxLatencyMsreturns the best results so far and reports the truncation rather
than erroring: a bound that returns nothing has not degraded, it has failed. - Configurable fusion weights via
retrieval.fusionWeightsor
REMEMBRA_RETRIEVAL_FUSION_WEIGHTS. Every default reproduces the previous
behaviour exactly. - A retrieval benchmark gate is now a release stage. §38 asked that every
retrieval change be measured; that was a rule people could forget. A 23-scenario
set now runs through the real pipeline and fails on a regression in precision,
recall, MRR, nDCG, duplicate rate or a latency percentile.
Fixed
- §37's budget was unreachable and its report was discarded.
MemoryService.searchneither accepted a budget nor returned the report — the
budget was computed and then dropped, so no caller could set a bound or learn
which one bound. Both fixed, and the budget is settable over HTTP as
?budget.maxItems=and friends, with a malformed bound rejected rather than
silently ignored. - The relation-expansion re-rank built its search policy separately from the
first search, so a configured fusion weight could apply to one ranking and
silently not the other.
Known limitations
- Non-ASCII queries return no lexical results.
extractQueryfilters query
terms with/^[a-z0-9]+$/u, so a query in any non-ASCII script produces zero
terms and retrieval falls back to the vector path alone. The CJK branch in
tokenizeis therefore unreachable from the query path. Documents in those
scripts are stored and tokenised correctly; querying is what is affected. Not
fixed here because the one-line change alters what existing deployments get back
for non-ASCII input. Full analysis indocs/v5.7.0-audit.md(S7).
Unchanged contracts
Four runtime dependencies, unchanged: @modelcontextprotocol/sdk,
better-sqlite3, yaml, zod. redis remains an optional peer and is not even a
devDependency. No new subpath, no removed export, /health/ready byte-identical to
5.6.0, and every V4.9/V5.0.3 security boundary intact.
Gates
923 test suite · 142 security matrix · 52 recovery matrix · Python SDK ·
47 documentation files · 23 benchmark scenarios · 0 audit findings · 0 npm
vulnerabilities. Full release:check passed for @hilbras/remembra@5.7.0, on
Node 18.20.8 and Node 24.21.0.
Remembra 5.6.0 — distributed Remembra
Distributed Remembra: cross-instance primitives, an optional Redis backend, and an
explicit process split. A single-process installation is unaffected — with no
REMEMBRA_REDIS_URL the optional package is never imported, no shared store is
opened, and the readiness payload is byte-identical to 5.5.1.
Runtime dependencies are unchanged at four. redis is an optional peer and is
not even a devDependency, which is what keeps its missing-package startup failure
a real test rather than a mocked one. Six new published subpaths: ./lock,
./job-store, ./worker, ./redis, ./shared-state, ./process-roles.
Not verified: a live Redis server. The quota scripts are executed through a
Lua 5.3 VM with a shim of the Redis commands they use (28 checks, covering their
logic but not a real server), and the failure matrix verifies our response to a
store outage rather than Redis itself. Treat shared-state operation as
reviewed-and-unit-tested until a Redis instance is in CI.
Added
-
The §30 failure matrix (
src/test/failure-matrix.test.ts, with a real
second OS process insrc/test/matrix-peer.ts): all nine distributed-failure
scenarios, each asserting a stated invariant rather than "did not crash". The
two-process rows force the same expected version into both processes rather
than relying on interleaving, so they detect the lost-update defect that
motivated the milestone. A live Redis is still not exercised — the
outage rows verify our response to a store outage, not Redis itself. -
Process roles (
src/process-roles.ts, published as
@hilbras/remembra/process-roles):remembra serve,remembra worker, and
remembra scheduler. The no-subcommand path is unchanged and still requires no
configuration, so a single-process install is unaffected. A role without an HTTP
surface refuses--http/--portand refuses data verbs rather than ignoring
them — aworkerthat silently bound a port would serve traffic from a process
whose premise is that it has no HTTP surface.workerruns a durable worker over
a SQLite ledger;scheduleronly enqueues periodic jobs, so exactly one worker
anywhere claims each one. -
Shared-state configuration with honest degradation
(src/shared-state.ts, published as@hilbras/remembra/shared-state). The
capability manifest advertisesdistributedunconditionally — it describes the
build, likewebhooks, not the configuration. A single-process readiness
payload is byte-identical to the previous release: it does not even gain a field
saying "absent". When a configured store becomes unreachable the limiter
fails closed with 503 rather than degrading to per-instance limits, which
would let a fleet exceed a tenant's quota while every instance reported a limit
it was not enforcing; readiness reportsunreadyso a load balancer drains the
instance, liveness stays up so it can be diagnosed, and it recovers on its own
at the next successful operation. Reports carry a classified label only — never
the URL, host, port, or password. -
An optional Redis adapter (
src/redis.ts, published as
@hilbras/remembra/redis) providingRedisLockProviderand
RedisQuotaRateLimiter.redisis an optional peer dependency and not a
runtime dependency; the only reference to it is a dynamicimport(). There is
no silent fallback: withREMEMBRA_REDIS_URLunset the process imports no
Redis code at all, and with it set but unreachable, startup fails rather than
degrading to per-instance state that would report a limit it is not enforcing.
Locks useSET … NX PXso the lease and its expiry are one atomic operation,
and renew/release are owner-checked by the server.
Quota is evaluated as one Lua script across every dimension rather than one
limiter per dimension, because the in-process limiter's mutex does not cross
instances and interleaved dimension checks would let a shared organization
budget be exceeded. Precedence and policy validation match the in-process
limiter exactly, and each dimension keeps its own window.
The script bodies are verified by running the shipped text through a Lua 5.3 VM
(scripts/verify-redis-lua.mjs, 28 checks, not a release gate). A live Redis
server is not exercised by the test suite. -
A durable worker (
src/durable-worker.ts, published as
@hilbras/remembra/worker), the shared-execution counterpart to the in-memory
JobQueue, which is unchanged and remains what a single process uses. Its
centrepiece is the partition guard: a claimed job's lease is renewed while it
runs, and if a renewal ever fails the job'sAbortSignalfires — finishing
anyway would be the lost-update failure below wearing a lease. A refused
complete()is reported aslease_lostrather than as success. Claims are
restricted to the types a worker declares, so a heterogeneous fleet never
claims work it cannot run, andstop()releases in-flight leases so a peer
takes over immediately instead of waiting out the lease. The poll timer is
deliberately notunref'd, since §28 allows a worker-only process that has
nothing else holding its event loop open. -
A durable job ledger (
src/job-store.ts, published as
@hilbras/remembra/job-store), the second distributed primitive. All six §27
states and ten fields, withInMemoryJobStoreandSqliteJobStorebehind one
JobStoreinterface. A claim is a singleUPDATE ... RETURNING, so the
database picks the winner and returns the row it actually claimed — a
read-then-write is the shape that produced the lost update below. Claims are
ordered oldest-first with the id as tie-break, so every claimer agrees on the
next job. A dead worker's job returns toretryingonce its lease expires, and
renew/complete/failreturnfalsefor a lease the caller no longer
holds. Payloads, job types, tenant digests, and stored error labels are all
bounded. 28 tests, with the whole behavioural suite run against both
implementations so they cannot drift. -
Lease-based locking (
src/lock.ts, published as@hilbras/remembra/lock),
the first primitive of the distributed-runtime milestone. Every acquisition is a
lease with an owner and an absolute expiry rather than a lock, so a crashed
holder is reclaimed instead of deadlocking.release()is owner-checked and
cannot clear a peer's lease;renew()returnsfalseonce a lease is lost, so
a partitioned worker stops rather than resuming on a belief it still holds it —
the same failure as the lost update below, wearing a lock. Two implementations
ship: an in-process provider (the default) and a file-backed one that reclaims
on the expiry timestamp rather than pid liveness, which is what makes it
portable beyond one host.lockKeyhashes any non-digest part, so a raw tenant
cannot reach a key that is written to disk. Documented in
docs/lock.md. 15 tests, two of which caught defects in the
implementation: an unreadable lease file livelockedacquireforever, and
isHeld()never observed the lease it had just written.
Fixed
-
A cold-start race in the batch idempotency ledger. Two processes
starting against a fresh ledger directory could observe each other's
not-yet-0600claims.sqlite— better-sqlite3 creates the file with the
process umask and it was onlychmoded after the open — and one would refuse to
start with "claim database must not be group/world accessible". The permission
check is unchanged; the file is now created0600withO_CREAT|O_EXCLbefore
it is opened, so there is no window to lose. Without this a fleet cannot
cold-start against a fresh store. -
A spurious
IO_ERRORfrom a racing delete on the file backend. Two
MemoryStoreinstances deleting the same memory could have one unlink the file
after the other resolved it; the resultingENOENTsurfaced as an error to a
caller that had done nothing wrong.ENOENTon the unlink is now the
already-reported "missing" result, so a racing delete is genuinely idempotent.
This was a pre-existing intermittent failure, caught by a release gate and
disproving the audit's own assessment offorgetas safe — idempotent state
is not the same as a non-throwing call. -
A cross-instance lost update on the SQLite backend, with a success
response.MemoryBackendrequires mutating calls to be safe under
"cross-process concurrency (advisory lock)", andSqliteBackendonly
satisfied the same-process half:withLockis a promise queue over one
instance's field, with noBEGIN IMMEDIATEand nodb.transaction()anywhere
in the file.updateread the row, checkedexpectedVersionin application
code, and then wroteWHERE id = ?with no version predicate — so two
instances that read the same version both passed the check and both wrote.
Reproduced 5 times out of 5 with two instances over onedata.sqlite: both
were told they wrote version 2, and one write was discarded with no error.
The write is now a compare-and-swap on the version it read, and a zero-row
result distinguishesNOT_FOUNDfromCONFLICTso a caller does not retry a
write whose row is gone. Six tests insrc/test/sqlite-cas.test.tscover the
race, the error text, delete-in-between, and the two behaviours that must not
change. Found by
v5.6.0-audit.mdfinding S1, the first task of the
distributed-runtime milestone — reachable before any distributed work, and
routine after it.
Known limitation, now measured
The native RemoveEnvironmentCleanupHook abort previously documented as a test
teardown nuisance can kill a running server on Node 24. Measured against the
published 5.4.0 and 5.5.1 packages under 2 500 requests of mixed write,
search, and scrape traffic:
| Runtime | Result |
|---|---|
| Node 18.20.8 | 2 000 requests, n... |
Remembra 5.5.1 — log field-policy fixes
Remembra 5.5.1 — a patch release for two defects in the log field policy
introduced by 5.5.0.
Both were found by a post-release audit that enumerated every logEvent field
name in the tree and diffed it against the policy's lists, rather than by reading
the code — in both cases the call site looks perfectly correct.
npm install @hilbras/remembra@5.5.1
pip install hilbras-remembra==5.5.1A documented diagnostic was silently removed
file sat on the blanket drop list alongside path and root. So
memory_parse_skipped and memory_normalized stopped emitting the file field
that the documentation advertises:
5.5.0 {"event":"memory_parse_skipped","msg":"...memory file bad.md"} ← no file field
5.5.1 {"event":"memory_parse_skipped","file":"bad.md","reason":"missing frontmatter"}
The event still said a memory file was unparseable, but not which one — which is
the only thing an operator needs from it.
A bare basename is not a disclosure; a path is. Filename fields now pass through
only when the value is bounded, separator-free, and free of parent references, so
/home/someone/.remembra/secret.md, ../../etc/passwd, C:\Users\..., and an
over-long name are all still refused in a file field.
The policy only inspected top-level keys
applyLogFieldPolicy ran once on the caller's field object, so a nested path
walked straight through:
logEvent("warn", "x", { details: { path: "/home/someone/..." } })
5.5.0 {"event":"x","details":{"path":"/home/someone/..."}} ← leaked
5.5.1 {"event":"x","details":{}}
The redactor was already recursive; the policy was not. It is now applied at
every depth — three levels down, and inside objects held in arrays — with the
top level left alone so an identity is not hashed twice.
Also in this release
The roadmap's renumbering table — added at the 5.5.0 release — was garbled, was
missing the V5.2 and V5.3 slots entirely, and claimed a shift its own table did
not describe. It is corrected, and the compatibility report matches it.
docs/observability.md notes the policy's history so the next reader knows the
rules were tightened after the fact.
Release evidence
| Gate | Result |
|---|---|
| Build | pass |
| Full suite | 688 tests, 0 failures |
| Security matrix | 142 tests, 0 failures |
| Recovery matrix | 52 tests, 0 failures |
| Python SDK | 28 tests, 0 failures |
| Documentation links | 41 files, 0 broken |
npm audit --omit=dev |
0 vulnerabilities |
| Node runtimes | 18.20.8 and 24.21.0 |
| Registry install | verified from npm and PyPI after publication |
Compatibility
No route, environment variable, metric, or default changes. The only difference
from 5.5.0 is which log fields survive. ^5.5.0 consumers pick this up
automatically.
Remembra 5.5.0 — production infrastructure
Remembra 5.5.0 — production infrastructure.
This is the roadmap §17–§24 milestone the plan calls V5.1.0. It ships as
5.5.0 because V5.4.0 was released first, so the planned 5.1.0 is now a
downgrade of an already-published version. The roadmap carries a renumbering
banner so the shift is explicit; milestone intent is unchanged.
Install
npm install @hilbras/remembra@5.5.0
pip install hilbras-remembra==5.5.0What is new
Rate limiting — a RateLimiter interface (check / consume / reset,
async) with a bounded in-process implementation, injected through
HttpOptions.rateLimiter. check decides without charging, so a caller can ask
whether it has budget without spending it.
Quotas — REMEMBRA_QUOTAS sets per-dimension policies across organization,
project, user, agent, API key, IP, endpoint, and provider, layered on top of the
base budget. Precedence is the declared dimension order, charging is
all-or-nothing, and a 429 now names the dimension that refused it without ever
naming a principal.
Health — /health/live, /health/ready, /health/storage, and
/health/provider. Liveness is public and reads process state only, so it stays
200 when storage is broken and reports draining once a shutdown begins.
Metrics — p50/p95/p99 over recorded histograms, the six series §21 named that
did not exist, and a hard bound on label cardinality.
Logging — every line carries requestId, operation, and durationMs
through an AsyncLocalStorage request context, so events from the service,
storage, provider, and job-queue layers correlate without threading anything by
hand.
Shutdown — a bounded, idempotent, honest sequence in both HTTP and MCP mode.
Eight defects found and fixed
This release opened with an audit, and the two with the most weight:
The rate limiter was an unbounded memory-exhaustion vector, reachable without
a credential. A window was reclaimed only if its own key was checked again
after expiry, so any identity used once and abandoned lived for the process
lifetime — 50,000 abandoned keys retained 50,001 entries. The anonymous bucket is
charged before authentication and keyed by source address. Now capped at
10,000 with a deterministic sweep and least-recently-used eviction.
A concurrent burst bypassed the quota entirely. Decide and charge were
separated by an await, so 500 simultaneous requests against an organization
cap of 20 were all admitted. A rate limiter that can be defeated by sending
requests in parallel is not a rate limiter. Found by the stress matrix, fixed by
serializing decide-and-charge, pinned by three regression tests.
The other six: /health performed a full storage scan and a durable
recovery-state write on every unauthenticated call; /metrics was rate limited
against its own documentation, so a scraper lost observability during the storms
it exists to diagnose; seven metrics were declared and never written, so
dashboards on them rendered empty and looked like data loss; the retrieval debug
trace logged raw query text behind the wrong environment flag and
migration_start logged a storage path unconditionally; MCP mode installed no
signal handler at all, so SIGTERM killed it with no drain; and an injected
embedding adapter bypassed provider metrics entirely — the path V5.4 most wanted
working.
Two of the test's own premises were also wrong, and were corrected rather than
papered over: refused requests under concurrency turned out to be 429 from the
default budget rather than the concurrency cap, and a 14 KB payload that appeared
to lose data was the token rule of the default sensitive-data policy matching
hyphen-separated words — a shape judgement, not a size limit.
Release evidence
| Gate | Result |
|---|---|
| Build | pass |
| Full suite | 684 tests, 0 failures |
| Security matrix | 142 tests, 0 failures |
| Recovery matrix | 52 tests, 0 failures |
| Python SDK | 28 tests, 0 failures |
| Documentation links | 40 files, 0 broken |
npm audit --omit=dev |
0 vulnerabilities |
| Package dry run | 249 files, 564.4 kB |
| Tenant benchmark | pass (within documented ceilings) |
| Scale benchmark | pass (within documented ceilings) |
| Node runtimes | 18.20.8 and 24.21.0 |
Not claimed
No distributed quotas, no distributed workers, no shared-state runtime, and no
advanced retrieval engine. The rate limiter, quota ledger, webhook queue, and
idempotency ledger remain single-host and in-process. The interfaces added here
exist so a shared implementation can be added later without changing the
request path — adding one is later work and is not claimed.
Compatibility
Additive everywhere. Authentication still precedes rate-limit consumption, rate
identity is still an opaque digest (now enforced rather than merely
conventional), REMEMBRA_RATE_LIMIT and REMEMBRA_RATE_WINDOW_MS still
configure the base budget, /health returns the same shape and status codes, and
SEC-RL-001, SEC-RL-002, and SEC-LOG-001 pass unmodified.
Four new limits a deployment could notice are listed in
docs/v5.5.0-compatibility.md.
Remembra 5.4.0 — Developer platform
Remembra 5.4.0 — Developer platform
Remembra is external memory for AI assistants. This release makes it
straightforward to integrate: explicit API versioning, hardened SDKs in two
languages, frozen batch semantics with durable idempotency, signed webhook
delivery, and a machine-readable contract with a verified example suite.
It is additive. Existing /api/v1 paths, legacy routes, and response bodies
are unchanged, and no upgrade action is required.
What is new
- Versioned API metadata —
GET /api/v1/capabilitiesand an
X-Remembra-API-Versionheader, with/api/v1remaining the canonical
namespace. No second/v1alias. - Hardened TypeScript SDK — typed snapshot create/restore, bounded timeout
composition, request-ID propagation, typed network-error normalization,
opt-in read-only retries, stable opaque cursor pagination, and a client-side
batch budget guard. - Frozen batch contract — explicit per-item and read-only execution
metadata, sanitized per-item failures, bounded read-only batch search, and a
transactional SQLiteIdempotency-Keyledger with current-authorization
preflight and recheck, integrity-protected rows, non-expiring claims,
capacity limits, and a durable data gate. - Signed webhook delivery (
@hilbras/remembra/webhooks) — a closed event
set, allowlist-only payloads with no embedding vectors or credential-like
fields, HMAC-SHA256t,v1signatures with a bounded timestamp window, a
receiver replay guard, an integrity-protected durable queue, and bounded
exponential backoff. Opt-in throughREMEMBRA_WEBHOOKS. - Receiver examples — the delivery contract is only useful if the
subscriber checks it, so verified Node and Python receivers ship alongside
the sender. Each reads the raw body, verifiesx-remembra-signatureagainst
those exact bytes, and rejects a repeated delivery id. An end-to-end test
drives the real CLI into the real receiver and confirms a forged body, an
expired timestamp, a foreign secret, and a replayed delivery id are each
refused. - Dependency-free Python SDK (
hilbras-remembra) — synchronous and
asynchronous clients with the same routes, authentication, error hierarchy,
retry policy, pagination, and idempotency rules, using only the standard
library. A parity test reads the published TypeScript contract so the two
SDKs cannot drift apart. - OpenAPI 3.1 contract and examples —
docs/openapi.yamldocuments the
full/api/v1surface, with a regression test that fails when a served
route, limit, idempotency rule, or capability is undocumented. Runnable
examples cover Node, Python, FastAPI, RAG, local-LLM, Next.js, React, and an
AI agent. - An honest CLI —
remembra --versionandremembra --helpanswer before
any configuration validation, storage-directory creation, or server start.
Safety properties that are now enforced
- The durable data gate records its owner. A data restore is completed with
remembra recover verify; an interrupted tenant migration stays resumable
throughmigrate apply, and verification refuses to publish a half-applied
migration. - An unsafe or replaced gate marker is never auto-cleared and can no longer
discard replay claims. - A released keyed-batch claim keeps binding the key to its original operation,
so a released key can never be reused for a different operation. - A pre-existing claim ledger is verified row by row and rebuilt in place, so
replay history survives an upgrade while tampering still fails closed. - An unreadable durable recovery state fails that one write closed without
latchingFailedfor every later write and read. - Webhook payloads never carry embedding vectors, provenance internals, or
credential-like fields, and a failed delivery can never fail the write that
produced the event.
Gate evidence
Verified on Node 18.20.8 and Node 24.21.0:
| Gate | Result |
|---|---|
| Build | pass |
| Full suite | 567 tests, 0 failures |
| Security matrix | 139 tests, 0 failures |
| Recovery matrix | 52 tests, 0 failures |
| Python SDK | 28 tests, 0 failures |
| Documentation links | 39 files, 0 broken |
npm audit --omit=dev |
0 vulnerabilities |
| Package dry run | 241 files |
| Registry install | npm install @hilbras/remembra@5.4.0; every subpath imports and a signature verifies |
| PyPI publication | pip install hilbras-remembra==5.4.0; zero dependencies, identical batch limits |
| Artifact identity | published wheel and sdist hash-match the artifacts built from this commit |
| Tenant and scale benchmarks | within documented ceilings |
Deliberate non-claims
V5.4.0 does not claim distributed quotas, distributed workers, or the advanced
retrieval engine — V5.1 through V5.3 remain unfinished. Public batch embedding
remains unavailable, idempotency and webhook delivery state are single-host,
and no V5.5 tenancy or V6 runtime work is included.
Known limitation
The pinned better-sqlite3 11.x binding can abort a test process during
environment teardown after every assertion has reported. The test scripts
re-run only the aborted files, log each recovery, and still fail immediately on
any real failure. Upgrading past 11.x requires Node 20+, which conflicts with
this release line's Node 18 gate.
Documentation
- Compatibility report:
docs/v5.4.0-compatibility.md - Public API:
docs/public-api.md - TypeScript SDK:
docs/sdk.md - Python SDK:
docs/python.md - Examples:
docs/examples.md - OpenAPI:
docs/openapi.yaml
Thanks to everyone who reported issues and tested the pre-release builds.
v5.0.3
Remembra v5.0.3
Reliability and recovery hardening for the V5 line.
Highlights
- Deterministic startup validation and explicit
Healthy/Degraded/Recovering/Failed/ReadOnlyhealth states. - Durable, atomically replaced recovery state with
remembra recover read-onlyandremembra recover verify. - Snapshot semantic validation, SQLite restore journaling/reconciliation, atomic SQLite batch imports, and durable file-import rollback journals.
- Permanent recovery coverage for restart, corruption, symlinks, SIGKILL, permission, disk, I/O, and SQLite-full failures.
- V4.9/V5 compatibility preserved; V5.1 and V6 remain deferred.
Verification
- Node 18.20.8 and Node 24.21.0 release gates passed.
- Full suite: 466 passed, 0 failed.
- Security matrix: 64 passed; recovery matrix: 48 passed.
- Documentation: 36 files; dependency audit: 0 vulnerabilities.
- Package dry-run: 222 files, 440.7 kB packed / 1.9 MB unpacked.
Remembra v5.0.2 — Authorization and Tenant-Isolation Hardening
Remembra 5.0.2
V5.0.2 hardens V5 authorization consistency and tenant isolation while preserving V4.9/V5 compatibility. It does not introduce V6 policy, schema, provider, API, or migration implementation.
Security and correctness
- Adds a centralized, pure authorization evaluator with explicit operation/capability mappings and non-informative denials.
- Enforces conjunctive organization/project/user/agent visibility across file and SQLite point reads, candidate SQL/counts, search/context, history, audit, entities, batches, snapshots, and scoped migration.
- Prevents administrative capabilities from erasing explicit project/user/agent selectors; organization-wide access is explicit.
- Requires
tenant:exportfor full and selected exports and rechecks configured tenant membership before ordinary service work, provider calls, and mutations. - Normalizes reserved identity ingress across HTTP/MCP/SDK surfaces and sanitizes provider/IO errors at public boundaries.
- Applies configured sensitive-data redaction to store, update, and import paths.
- Preserves the V5.0.1 fail-closed storage, history containment, signed snapshot, and idempotent migration guarantees.
Documentation
- Finalizes the V5 threat model with assets, trust boundaries, attackers, attack surfaces, controls, residual risks, operator responsibilities, and permanent regression evidence.
- Documents the exact file-backend, SQLite, snapshot, backup, and transport encryption boundaries; strict mode refuses legacy global Markdown/encryption commands.
- Adds the V5.0.2 authorization contract and clarifies preflight-versus-atomic snapshot restore behavior.
Verification
- Node 18.20.8: complete release gate passed.
- Node 24.21.0: complete release gate passed.
- Full suite: 431 passed, 0 failed; security matrix: 64 passed; recovery matrix: 13 passed.
- Documentation check: 36 files passed; dependency audit: 0 vulnerabilities.
- Package dry-run: 207 files, 422.7 kB compressed; 10K/100K tenant and bounded scale benchmarks passed.
Upgrade and rollback guidance
Read the V5.0.2 authorization contract, V5 threat model, and security/self-hosting guidance before enabling strict tenant operation. Keep a verified backup before applying migrations; failed per-record restore operations are idempotent but may require backup restoration or a verified retry.
Artifacts
- npm: https://www.npmjs.com/package/@hilbras/remembra/v/5.0.2
- Git tag: https://github.com/Hilbras/Remembra/releases/tag/v5.0.2
- Source commit:
be51dae72556870be82fdee60d2cb8680ae5e5f0
Remembra v5.0.1 — Security and Migration Hotfix
Remembra 5.0.1
V5.0.1 is the security, storage, history, and tenant-snapshot correctness hotfix for the V5 platform. It preserves V4.9/V5 compatibility and does not introduce V6 behavior.
Security and reliability fixes
- Authenticates requests before consuming protected rate-limit buckets; anonymous and authenticated identities are isolated and opaque.
- Adds centralized secret redaction for structured logs, debug search diagnostics, provider errors, nested headers, and credentials; public provider errors are sanitized.
- Makes SQLite initialization and startup migration readiness fail closed. The file backend is available only with
REMEMBRA_ALLOW_FILE_FALLBACK=1, is warned about, and is reported by health. - Validates and contains file-history paths and rejects symlinked history storage/sidecars.
- Rejects tenantless snapshots during ordinary strict restore before any write.
Explicit tenant migration
- Adds read-only
remembra migrate analyze. - Adds signed, target-bound
remembra migrate planandmigrate apply --dry-run/apply commands. - Requires a trusted strict operator tenant and signed snapshot key; ordinary import cannot enable migration.
- Verifies plan signatures, snapshot digests, manifests, and destination tenant scope; repeated applies are idempotent.
Verification
- Node 18.20.8: complete release gate passed.
- Node 24.21.0: complete release gate passed.
- Full suite: 414 passed, 0 failed; security matrix: 47 passed; recovery matrix: 13 passed.
- Documentation check: 35 files passed; dependency audit: 0 vulnerabilities.
- Package dry-run: 203 files, approximately 410 kB compressed.
Upgrade and rollback guidance
Read the V5.0.1 security and migration guide before enabling strict tenant restore or explicit migration. Keep a verified backup before applying a tenant plan; a failed migration must be investigated and restored/retried with the same signed, target-bound plan.
Artifacts
- npm: https://www.npmjs.com/package/@hilbras/remembra/v/5.0.1
- Git tag: https://github.com/Hilbras/Remembra/releases/tag/v5.0.1
- Source commit:
ba3be3e0ca280a6caf214465c5359f4488c2eea9
Remembra v5.0.0 — Production Memory Platform
Remembra 5.0.0
Remembra 5 is the production memory platform release, preserving V4.9 compatibility while adding tenant-safe context and retrieval.
Highlights
- Adds deterministic
memory_contextassembly with a 4,000-token default budget, hard 100,000-token ceiling, bounded candidates, authorized retrieval, and vector omission from responses. - Adds trusted host-resolved tenant contexts, strict/legacy tenant modes, versioned organization membership, organization/user/project/agent entity services, trusted HTTP/SDK routes, and fail-closed isolation across storage, retrieval, relations, history, audit, jobs, snapshots, migrations, and recovery.
- Adds effective embedding reranking, bounded pool-local relation expansion, cache partitions, and deterministic ordering.
- Adds signed snapshot and migration manifests, checksum/reference preflight, durable checkpoints/resume/failure records, verified SQLite backup/restore, retained-previous rollback, and corruption/symlink/sidecar rejection.
- Preserves all eleven memory types, Markdown compatibility, V4.9 clients, and the thirteen existing MCP tools;
memory_contextis additive and the MCP manifest version is2. - Adds reproducible release gates for Node 18 and Node 24, including full tests, security/recovery matrices, documentation, audit, package contents, and 10K/100K tenant benchmarks.
Verification
- Node 18.20.8: full release gate passed at 5.0.0.
- Node 24.21.0: full release gate passed at 5.0.0.
- Strict tenant benchmark: 10,000 p95 50.87 ms; 100,000 p95 398.81 ms in the final gate environment, below the documented ceilings.
- Dependency audit: zero vulnerabilities; package dry-run and export checks passed.