[1.27.25] — 2026-08-19
Release notes
Security fixes
- The graph-PPR third recall leg is now scoped like the vector and FTS
legs. It applies the domain label,access_scope, owner, memory-kind and
retention predicates via the same shared SQL builder (push_gate_filters),
and carriesk.piiinto the hit so the read seam redacts graph hits
exactly like the other legs. Before this, the leg (unreleased default-on)
ignored every filter and hardcodedpii: false— a cross-domain,
cross-owner, unredacted side door on/recall,/search, and
/ump/recallin shim mode (pass-3 S3-01, CRITICAL). Pinned by
graph_leg_scopes_domain_and_owner_s3_01+
graph_leg_empty_permit_and_pii_carry_s3_01(two-domain shared-entity
fixture — the exact collision shape of the finding). /verifybinds theX-Brain-Domainlabel in SQL + the record gate
(the/get/{id}idiom): a foreign-domain chunk id now reads as not-found
instead of answering "supported" as a cross-domain content-confirmation
oracle (S2-09). Pinned byverify_cannot_cross_domain.GET /ump/memory/{id}binds the domain label + record gate — the
MCP-reachable (ump.get) surface no longer renders any row by bare id
under a global read grant (S2-10). Pinned byump_get_memory_cannot_cross_domain.GET /procedure/{id}/stepsbinds the domain label + record gate (S2-30).GET /domains/{name}/exportrequires Admin in shim mode — the snapshot
resolves to the ONE shared pool there (every tenant's chunks, owners, the
audit chain), which a per-name Read grant must never cover. Multi-db keeps
Read (the file IS the domain). TheVACUUM INTOpath now goes through the
shared quote-escaping primitive (S2-08/S2-24).- The rate limiter moved OUTSIDE the auth layers. An unauthenticated
flood is now 429-throttled before any token work — previously it
401-rejected before ever consuming a bucket, and each free 401 performed a
synchronous audit write on a fresh connection (unthrottled
DB-write-per-request amplification). The deny-path audit writes now run on
spawn_blocking(S3-03). Pinned byrate_limit_layer_is_outside_auth_layers. GET /graph/relationships/{id}/historygates onAction::Admin,
matching what every doc surface (CHANGELOG §1.27.22, openapi.yaml,
docs/api.md, its own doc comments) already claimed — the retired
PII-bearing entity labels it returns are operator evidence. The read-audit
failure is no longer silent (S3-02)./addwrites the quarantine flag IN-TX, before the commit — a failed
flag write now rolls the whole chunk back (the/ingest/memoryposture)
instead of leaving the injection chunk durably storedflagged = 0while
telling the caller it failed (S3-06)./suggestapplies the v1.14 scope filter + v1.23 role gate like
/recall— an owner-restricted role no longer sees other owners' private
rows as suggestions (S2-29).- Smaller hardening:
X-Forwarded-Fortrusts the RIGHTMOST entry under
BRAIN_TRUST_PROXY=1(leftmost is client-spoofable; S2-39); the rate
limiter fails CLOSED on a poisoned lock (S2-50); the dead
"developer mode"blocklist entry now matches (whitespace is stripped
pre-match; S2-44); the audit-chain BEGIN-failure path bumps
audit_commit_failures(it was silent; S3-09); the two boot-time
VACUUM INTOliterals go through the escaped primitive (S3-11).
Security fixes (wave 2 — the deferred items)
- The audit retention prune now VERIFIES before it prunes and records a
retentionevidence row for what it deleted — previously the re-anchor
would have re-blessed a tampered chain into a freshly-verifying one
(evidence laundering), and the deletion of audit evidence was itself
unevidenced. A failed re-anchor UPDATE now rolls the whole prune back
instead of committing a half-rewritten chain (S2-16 + S2-35). verify_chainenforces the NULL-prefix rule (F-03, the no-hash-change
half): a NULLprev_hashis legal only before the chain starts. Legitimate
writers always chain from the tip once one exists, so a mid-chain NULL is
tamper — previously it was skipped silently at any position. No stored hash
changes.brain restorere-applies ACTIVE legal holds from the pre-restore DB
and loudly discloses tombstoned content the backup resurrected — a pre-hold
backup no longer silently unfreezes litigation-held ids, and an undone
DSAR purge is on the record (S2-28).- The open-edge invariant is structural:
idx_rels_open_unique(partial
UNIQUE on the tripleWHERE superseded_at IS NULL, after a deterministic
newest-wins dedup of legacy double-open rows) — a racing double-insert now
fails at the DB and rolls back the ingest instead of corrupting the
lineage (S3-08; schema → 1.27.25). - The remaining shim-mode reads are scoped:
/decayed+/quarantine
bind theX-Brain-Domainlabel in SQL;/statscounts by domain label
(entities/relationships via their chunk linkage);/consolidate/propose
requires Admin in shim mode (its five detection scans are corpus-wide);
the domain-registrydomain_invaliderror no longer embeds the
known_domainsinventory (S2-31/43/32). - Ingest auto-routing re-authorizes on the ACTUAL target — a
write:<t>/global-only principal can no longer contaminate another
tenant's domain through centroid routing (S2-33). /clientsdenies empty-grant auditors at the gate (403, not a silent
200-empty — "Some([]) denies all" now means the surface too; S2-15).- The DSAR certificate's remanence claim follows the pragma attempt — on
a failedsecure_delete=ONit downgrades to the disclosed logical posture
instead of certifying an overwrite that never ran (S2-18). - Chunker fidelity: an UNTERMINATED oversized fenced block no longer
duplicates its final code line into every stored piece (the last line was
treated as a closer it wasn't); degenerate over-cap lines inside fences end
with a newline so re-attached closers sit at line starts; prose pieces stay
strict verbatim (S2-19/S2-20). - Evidence self-links are skipped in the batched enrichment (a
from == torow satisfied bothIN (…)groups and duplicated into API
responses; S2-38). Domain delete now archives tombstones +
evidence_links into the pre-delete segment alongside the audit rows — the
deletion registry is evidence and no longer dies with the domain (S2-21).
Bug fixes
- Plugin:
autoRecallGraph: falsedisables the graph leg again. The
flag previously OMITTED thegraphparam when false, so the server's
default-on change silently enabled the leg for every plugin user. The
flag is now always sent explicitly; the plugin's documented default stays
opt-in.
Improvements
openapi.yaml/health+/health/dbschemas now match the shipped
shapes (the public probe is{status, version}; the detailed body is
Read-gated on/health/db) — the contract previously documented the full
fingerprint body on the public route.SECURITY.mdegress inventory is
truthful (three enumerated, bounded, opt-in/gated paths — not "exactly
one").
Full Changelog: v1.27.24...v1.27.25