Skip to content

v1.27.25

Latest

Choose a tag to compare

@github-actions github-actions released this 19 Aug 13:17
· 5 commits to main since this release

[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 carries k.pii into 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 hardcoded pii: false — a cross-domain,
    cross-owner, unredacted side door on /recall, /search, and
    /ump/recall in 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).
  • /verify binds the X-Brain-Domain label 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 by verify_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 by ump_get_memory_cannot_cross_domain.
  • GET /procedure/{id}/steps binds the domain label + record gate (S2-30).
  • GET /domains/{name}/export requires 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). The VACUUM INTO path 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 by rate_limit_layer_is_outside_auth_layers.
  • GET /graph/relationships/{id}/history gates on Action::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).
  • /add writes the quarantine flag IN-TX, before the commit — a failed
    flag write now rolls the whole chunk back (the /ingest/memory posture)
    instead of leaving the injection chunk durably stored flagged = 0 while
    telling the caller it failed (S3-06).
  • /suggest applies 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-For trusts 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 INTO literals 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
    retention evidence 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_chain enforces the NULL-prefix rule (F-03, the no-hash-change
    half): a NULL prev_hash is 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 restore re-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 triple WHERE 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 the X-Brain-Domain label in SQL; /stats counts 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-registry domain_invalid error no longer embeds the
    known_domains inventory (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).
  • /clients denies 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 failed secure_delete=ON it 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 == to row satisfied both IN (…) 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: false disables the graph leg again. The
    flag previously OMITTED the graph param 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/db schemas 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.md egress inventory is
    truthful (three enumerated, bounded, opt-in/gated paths — not "exactly
    one").

Full Changelog: v1.27.24...v1.27.25