Persistent, consensus-validated institutional memory for AI agents. Runs locally.
io.github.l33tdawg/sage Model Context Protocol (MCP) Server
This MCP server provides persistent institutional memory for AI agents that persists across conversations. It validates stored memory via BFT consensus, associates confidence scores with memory, and decays naturally over time. It runs locally and is positioned as infrastructure rather than a flat file or a vector database bolted onto a chat application.
Persistent, consensus-validated memory infrastructure for AI agents.
SAGE gives AI agents institutional memory that persists across conversations, goes through consensus validation, carries confidence scores, and decays naturally over time. On a multi-validator network that validation is a BFT quorum; on a personal install it is the node's own signed vote. Not a flat file. Not a vector DB bolted onto a chat app. Infrastructure — built on the same consensus primitives as distributed ledgers.
If an MCP client launches this through a wrapper, point its stdio configuration
at the wrapper's absolute path. Pass SAGE_PROVIDER, SAGE_PROJECT, and
SAGE_IDENTITY_PATH through docker exec -e/docker compose exec -e; setting
them only on the host-side Docker command does not place them in the container.
Keep the whole SAGE data root mounted at /root/.sage, including agent keys and
the ledger. Do not start a separate docker run ... mcp container: its
localhost:8080 is isolated from the running SAGE server.
HTTP MCP is also available at /v1/mcp/sse and /v1/mcp/streamable, but both
require a bearer token or OAuth. Bare http://localhost:8080 is the REST base,
not an unauthenticated MCP endpoint.
Upgrading an existing node
Upgrading from an older version?
Upgrading an existing node — including the v10.x → v11 jump — is
docs/UPGRADING.md. In the desktop app, accept the update:
SAGE verifies canonical upgrade compatibility, captures a full recovery
snapshot, installs, and restarts automatically. Headless and quorum operators
have separate technical procedures in the guide.
Your chain advances in place; a personal node climbs the consensus fork ladder by
itself. Read the guide before a multi-admin chain crosses app-v23 — that
activation re-derives administrator authority.
If you installed SAGE before v5.0 and your AI isn't doing turn-by-turn memory updates, re-run the installer in your project directory:
bash
cd /path/to/your/project
sage-gui mcp install
This installs Claude Code hooks that prompt the memory lifecycle (boot, turn, reflect) — even if your .mcp.json is already configured. Restart your Claude Code session after running this.
Architecture
flowchart TB
A["AI agents · MCP / SDK / REST"] --> P["SAGE node · authenticated admission + live policy"]
H["CEREBRUM · local human control"] --> P
P --> M["Memory + local policy transactions<br/>CometBFT / ABCI"]
P --> W["Node-local coordination<br/>inbox / claims / replies"]
M --> B["BadgerDB<br/>authoritative chain state"]
B --> Q["Commit-time SQL projection<br/>content + vectors for authorized recall"]
P -. "explicit peer trust and sharing" .-> F["Separate SAGE chain<br/>bounded Read / receiver-controlled Copy"]
classDef entry fill:#eef2ff,stroke:#6366f1,color:#1e293b
classDef memory fill:#ecfdf5,stroke:#059669,color:#064e3b
classDef work fill:#fff7ed,stroke:#d97706,color:#7c2d12
class A,H,P entry
class M,B,Q memory
class W,F work
Agents are not validators. Personal mode runs one real CometBFT validator
with a per-node memory auto-voter; it has no Byzantine redundancy. Registering
more agents does not add consensus voters. A multi-validator deployment runs
one shared chain; federation connects separate chains under explicit policy.
Storage has two roles. BadgerDB is authoritative for consensus state.
SQLite (personal) or PostgreSQL + pgvector (cluster) projects memory content
and vectors at Commit. Node-local message coordination is separate from the
memory consensus path. Block inclusion is not the same as memory acceptance.
For the detailed trust boundaries, lifecycles, and deployment topology, see
Architecture & Deployment.
Current Capabilities
Capability
What it provides
Governed memory
Persistent, attributed memories with consensus validation, semantic recall, confidence, and lifecycle controls
Durable tasks
Exact-agent assigned backlog; open tasks do not decay; idempotent creation and workflow status
Unified inbox
Local/federated requests, assignment notices, and a separate passive reply page
Runtime handoff
Explicit session-and-revision-fenced takeover of claimed work within the same signed agent identity
Access controls
Active enrollment, roles/profiles, ownership, Access Groups, compatible grants, and classification checks
Controlled federation
Explicit agent exports and bounded Read/Copy policy, without granting local membership or Write
Recovery and updates
In-place chain upgrades, recovery snapshots, and retained message claims across ordinary restarts
How agents collaborate
flowchart TB
T["Task assigned to exact agent"] --> N["One-way assignment notice"]
N --> I["Unified inbox"]
R["Request addressed to exact agent"] --> I
I -->|"task notice"| V["Verify current assignment in backlog<br/>then update the task"]
I -->|"inbound request"| C["Claimed by one MCP runtime"]
C -->|"normal completion"| O["Idempotent reply"]
C -. "intentional same-agent takeover" .-> H["Handoff: expected session + revision"]
H --> O
O --> S["Original sender reads reply_items<br/>or pages retained replies"]
classDef input fill:#eef2ff,stroke:#6366f1,color:#1e293b
classDef task fill:#ecfdf5,stroke:#059669,color:#064e3b
classDef message fill:#fff7ed,stroke:#d97706,color:#7c2d12
class I input
class T,N,V task
class R,C,H,O,S message
Assignment, claim, and reply are different states. A task notice is not a
request for a message result, and a reply is not a new assignment. Runtime
handoff does not reassign a task to another agent. Wake notifications are
payload-free hints, not delivery or claim evidence. Every agent request and
result remains untrusted data, not authority to expand the user's instructions.
http://localhost:8080/ui/ — a dashboard-native operator console centered on the 3D MRI memory brain, with chain health, agents, federation, semantic memory, recall tuning, vault recovery, tasks, imports, and updates around it. Every major workflow is available from the browser; the CLI stays there for automation and recovery.
Control Board
Federation
Recall Engine
Chain health, quorum, agents, federation, and embeddings
One trust-only JOIN that prepares Direct and Secure relay automatically, followed by independent Read/Copy choices on each SAGE
Smart-memory setup, managed reranker install, and recall-depth tuning
The dashboard also includes governed agent enrollment, Access Groups, domain
permissions, separate CEREBRUM Root credential handover, import/export,
software updates, and encryption controls. Ordinary agent identity replacement
uses re-enrollment; historical memory authorship is preserved.
What's New in v11.23.0
App-v28 activates.maxSupportedAppVersion moves 27 → 28 and converges with the compiled ceiling, so the gate v11.22.0 shipped dormant becomes an automatic rung: a personal node now advances its own chain across the seam on upgrade with no governance ceremony, the same path every earlier rung took. What activates is the sparse public-memory Merkle commitment over committed PUBLIC=0 records — AppHash-covered through a composite rule that hashes the legacy tree without the index nodes and composes it with the index root — and the co-commit tombstone rule, now enforced by the consensus path behind a content-hash reverse index maintained by every memory write and backfilled at activation.
The state-sync item that blocked it is fixed, and the contract's evidence is complete. A v28 provider used to die on its state-sync serving boot: the verification family recomputed the pre-v28 AppHash rule unconditionally, so the composite root could never match, the process exited with persisted AppHash does not match Badger state, and the caller waited on a Comet RPC that never became ready. The rule is now selected in one function shared by the commit path and the verification path, so the two cannot disagree about which rule is in force. TestAppHashDeterminism_AppV28Activation walks a fresh four-validator devnet app-v2 → app-v28 one rung at a time with byte-identical AppHash at H-1/H/H+1 of every seam; both Consensus Fault Gates pass on this change; and the real-process state-sync gate drives its provider through app-v28 (TARGET_APP_VERSION=28), so a pristine receiver restores from the provider snapshot and both sides report exact app-v28 state with converging AppHash. The gate's receiver pre-publication SIGKILL and provider SIGKILL phases, plus the replay family that pins historical blocks to their original app versions, cover restart and replay equivalence across H.
The native shell rides this release. A minor bump is the release that widens the shipped shell's SSCP compatibility range, which now admits v11.23 daemons — so fixes after this one ship as patch releases under the same shell.
No chain reset and no transaction-type change; historical blocks keep replaying under their original app versions.
App-v28 stays dormant in this release. v11.22.0 shipped the public-memory commitment and the consensus-side co-commit tombstone rule as one compiled gate, with the auto-vote ceiling deliberately held at 27 until its evidence existed. That evidence is nearly complete — the four-validator determinism ladder crosses every seam to app-v28 with byte-identical AppHash, and the upgrade gate drives the whole ladder with auto-votes — but the contract's last item, a promoted node surviving state-sync and restore, currently fails: a v28 node restarted by the real-process state-sync gate comes back, serves, and then stops answering. The ceiling bump ships in the release that turns that green, so app-v27 remains the ceiling here and the deliberate path (propose plus explicit votes) is unchanged.
The Federation page now leads with your connections. Managing a link used to mean scrolling past a status rail, the master switch, a network-name editor, a connectome for every agent, an always-open pairing wizard and only then the connections themselves, and per-agent discovery hid behind a Save button inside an expanded panel. The page opens on Your trusted SAGEs instead. Each connection row states its own discovery posture — all agents visible, N agents visible, or no agents visible — from one bounded read per link, so the row answers without expanding. The connectome and sharing groups move into collapsed Explore agents and Sharing groups sections at the bottom, and the pairing wizard appears only when there are no connections or when Connect a SAGE is used.
Agent visibility is a switch, not a form. Every eligible agent on a connection's roster carries a Visible/Not-visible switch that saves immediately under the connection's revision-bound agreement. The switch works on the full roster, so hidden agents never vanish from the operator's list, and Show all agents / Hide all agents move the whole roster at once. The Save-then-Saved form is gone, and copy-address is demoted to a quiet secondary action on peer cards.
The SDK can set an agent's enrollment clearance.set_agent_access_policy(agent_id, role, profile, clearance, capabilities=0, home_domain=None) writes the atomic app-v23 policy endpoint, and get_access_state() reads the consensus-authoritative snapshot back, so a script verifies the write landed instead of inferring it. This closes a gap that reads as a bug: the memory-write gate compares a submission's classification against the agent's enrollment clearance, not its organization or department membership clearance, so an operator who granted clearance through membership saw every classification ≥ 2 write refused while get_profile() still reported clearance 1.
CEREBRUM's App version panel renders on every node. The panel (chain rung, both ceilings, the Propose button) was nested behind the governance-scope list, so it never rendered on a personal node — scope records only exist once a scope_action commits, and most nodes have none. It now renders on its own, and only the scope cards stay gated.
No consensus change and no chain reset. maxSupportedAppVersion stays 27.
App-v28 ships compiled and dormant, on purpose. Two consensus-visible changes now exist behind one gate. The sparse public-memory Merkle index over committed PUBLIC=0 records becomes AppHash-covered at the activation height, through a composite rule that hashes the legacy tree without the index nodes and composes it with the index root. The co-commit tombstone rule stops being a check at the local REST submission boundary and becomes a rule the consensus path enforces. Neither one is active: maxSupportedAppVersion stays 27, so every node's upgrade auto-voter abstains on v28 and a personal node cannot advance itself into the fork. Activation waits for the evidence the contract names — byte-identical AppHash across the seam on a four-validator devnet, both Consensus Fault Gates, a promoted node surviving state-sync and restore, and replay equivalence — and ships in the release that carries it. An explicitly proposed, quorum-approved plan can still activate it; nothing does so on its own.
The tombstone rule is a consensus rule now. A co-commit never consults the voter — block inclusion is decisive — so the content-hash dedup that keeps a rejected memory's exact bytes out of the store never ran on that path, and a directly broadcast envelope never met the REST guard at all. The predicate needs data consensus state did not carry: memory:<id> is a content hash plus status, so "did these exact bytes already leave proposed under a different id" had no reverse lookup. The fork adds one — an entry per content hash and id that has left proposed — maintained by every memory write, backfilled from existing records at activation, and consulted from H+1 with the candidate's own id excluded so an idempotent re-send is decided by its own record.
The binary now states two ceilings instead of one. What it can execute (app-v28, compiled) and what it will auto-vote (app-v27) are different numbers while a gate awaits its evidence, and the surfaces say which is which: upgrade status and upgrade preflight print both, the version banner and full-backup stamp follow the compiled ceiling, and state sync accepts a restore up to it, so a promoted node can be rebuilt from a snapshot. The acceptance gate and the authorization ceiling moved with them, and the invariant that used to require all three numbers to be equal now states what each one means.
Upgrades can be proposed and voted on from CEREBRUM, and voted on from the terminal. The Governance view gains an App version panel — the chain's rung, both ceilings, the pending plan, the active ballot, a Propose button for the next rung — and sage-gui upgrade vote casts an explicit accept, reject or abstain. That path matters because a dormant gate is designed so nothing auto-votes: reaching quorum is a deliberate act by validators, which is what the panel and the command exist for. The generic governance-propose route still refuses OpUpgrade by design; the dashboard speaks the dedicated UpgradePropose transaction instead.
No chain reset, no transaction-type change, and historical blocks keep replaying under their original app versions. app-v27 remains the ceiling until v11.22.0's dormant gate gets its evidence.
The 11.21 line opens, and the shell that admits it ships with it. The daemon is the v11.20.5 build: a peer whose address moved repairs its own route, a flapping peer's windows are spent on the backlog, and recall names the confidence floor that filtered a result instead of returning an empty answer. What is new is the line itself. The SSCP compatibility range lives in the shipped native shell, not in the daemon, so a minor bump is the release that has to widen it: the shell now accepts v11.10 through v11.21 daemons, which is what lets the fixes after this one ship as patch releases instead of a rebuilt shell each time. If you run the daemon and the desktop app as separate installs, update both — an 11.21 daemon under the 11.20.5 shell is refused control by design, and that refusal is the gate doing its job rather than a fault.
No consensus change or chain migration; app-v27 remains the ceiling.
A peer whose address moved repairs itself instead of waiting for a human. The R2 fix let a P2P-only agreement run the authenticated route exchange across trust generations, because withholding the route fallback left it no transport at all. The same assumption fails for an agreement paired with a concrete endpoint once that host moves: the address is real, so nothing looks unroutable, but every request dials a machine that no longer answers while the peer's own traffic keeps arriving — the asymmetry operators report as "they can reach us, we cannot reach them". Withholding the fallback there also blocked the exchange, which is the only thing that can replace the stale snapshot, so the pair could never recover without re-pairing. The exemption is now about the path rather than the agreement shape: the exchange may use a stale snapshot as an authenticated bootstrap hint for both, while every other request still refuses a cross-generation route, and the persisted result is revalidated against the exact agreement and binding before it is written. A moved host also no longer reads as an offline one: the failure carries the trust-generation recovery code, so the operator's next move is a route repair rather than a network investigation.
A window of reachability is spent on the backlog, not on one message. A peer that flaps hands out short windows, and the drain only attempts rows whose backoff has expired — so a fresh event, due immediately on its first attempt, consumed the window while older rows slept through it. That is exactly the shape of a message created at 21:15 delivering while messages from 21:10 and 21:11 stayed queued. A successful delivery now proves the peer reachable and makes the rest of that peer's backlog due at once (attempt counts and last errors untouched — only the sleep is cleared), and a drain pass covers sixteen rows rather than four, with concurrency unchanged.
Recall no longer hides results behind the confidence floor without saying so. A memory reachable by tag was invisible to semantic recall because the node's floor (85) sat above the confidence its records were written with (0.80): the floor is applied before ranking, so no query can compensate, and the response said nothing — the whole observation tier is unreachable on a node tuned that way. Recall now reports the floor it ran with, how many candidates the floor removed, and a one-line note naming the remedy and the tiers a floor above 0.80 or 0.60 hides; the REST envelope carries the same facts under filtered and the X-SAGE-Filter-Applied header, and the settings surface warns where the value is read and saved. A floor that removed nothing is disclosed too, because that is what lets an empty result be trusted.
No consensus change or chain migration; app-v27 remains the ceiling.
A signer fence now survives the process that raised it. The fence is in-process state, and its own documentation named the hole: a restart, crash or SIGKILL discarded it, after which the nonce allocator re-seeded each key from the highest committed nonce — below the abandoned one by definition, because "unresolved" is what unresolved means — and the next action signed into that gap. The abandoned transaction was refused Code 4 when it finally landed, and the loss was untraceable: an operator saw an unrelated later action fail as a replay. Every submission is now shadowed by a durable record written at the last boundary before the bytes reach the transport, retired only on a proven fate, and re-raised as a fence at startup — so a node that was killed mid-submission comes back refusing to sign that key instead of re-seeding past it. Measured against a real cluster: two validators held fences for 13 days while their transaction sat in neither chain nor mempool, and their restarts bought exactly one signature each because the discarded fence was the only record of it.
A nonce that is provably dead now has an exit. The fence's rule is that only proven fate lifts it, which left one shape it could not resolve on its own: a submission whose fate was never observed and whose signed bytes did not survive the process. POST /v1/dashboard/signer-fence/lift (behind the CEREBRUM operator gate) accepts two proofs and nothing weaker — the exact transaction found in a committed block, or supersession, meaning a higher nonce for that signer has already committed, which makes the fenced allocation permanently uncommittable under the consensus nonce rule. Both proofs are read by the node rather than asserted by the caller, and a superseded lift records that the fenced transaction's payload is permanently lost. A lookup miss stays unproven: CometBFT indexes a transaction only once it is in a block, so a mempool-resident transaction answers not-found exactly as it does one second before it commits.
What the record deliberately does not carry. Not the signed bytes. Those routinely hold memory content that must not be copied into a plaintext table, so a restored fence resolves by proven fate instead of by re-submission. And a crash between the record and the wire holds a key that may have nothing in flight — the safe direction, resolved through the same proof path.
No consensus change or chain migration; app-v27 remains the ceiling.
A peer's "too large" answer no longer kills the message. The federated outbox classified 413 alongside the 4xx statuses that mean the peer understood the request and refused the bytes, so a single refusal marked the transport event failed and the canonical message with it: the row is never scanned again, and a durable-until-handled message carries an expiry a century out that never relieves it. That verdict is wrong for this status. A peer's per-route body cap is a build-time constant that moves when the peer upgrades, and the refusal is frequently not about size at all — on 2026-09-15 a message was refused as too large while its signed body sat 2.6 KB under the route's 16 KiB cap, and a larger message to the same peer was accepted unchanged minutes later. 413 now retries on an hourly floor, like the other capability-shaped status, so the event stays pending and delivers once the peer can take it.
The listener stopped calling every body it could not read "too large". The federation gate read the request body and answered 413 for any error, which merged a genuine over-cap body with a truncated upload, a mid-body disconnect and a stream reset — and the sender's terminal 413 rule turned that mislabel into permanent loss. Only a real *http.MaxBytesError is 413 now; a body this node could not read is answered as a read failure, which stays retryable, and logged with its underlying cause.
A delivery failure is visible in the log. The transport worker logged only when recording a failure failed, so an event could die with nothing in the log to say why. Every failed attempt now carries the event, the peer, the kind, the attempt count, the verdict and the retry delay.
No consensus change or chain migration; app-v27 remains the ceiling.
A submission whose outcome the node could not observe is now reported as exactly that, instead of as a failure. Every REST submit waits for broadcast_tx_commit, and that wait can expire before the block lands — on a loaded cluster the transaction then commits seconds later, while the caller has already been told 500 Broadcast error, indistinguishable from a genuine internal fault. That was worse than unhelpful: a caller retrying "on error" re-signs, so one write can be applied twice. The endpoint now answers 202 with "status":"indeterminate", the exact tx_hash of the bytes that went on the wire, the allocated nonce, and "retryable":false, while the node's signer nonce fence keeps reconciling the real fate.
Definitive outcomes keep their verdicts. A CheckTx or FinalizeBlock rejection still returns the status it always did, and a full mempool still returns 429 with Retry-After — nothing was admitted, so there is nothing in flight to chase.
Generated testnets stop inheriting the wait that causes it.deploy/init-testnet.sh now writes timeout_broadcast_tx_commit explicitly (45s) rather than leaving CometBFT's 10s default, and keeps it strictly below SAGE's own client-side wait (SAGE_TX_COMMIT_TIMEOUT_MS, 60s) so the node — which knows whether it admitted the bytes and can name the transaction hash — is always the party that answers.
No consensus change or chain migration; app-v27 remains the ceiling.
Federated replies are deliverable for a week, not a day. The window a reply stays admissible, and the deadline its retained outbox event is retried until, move from 24 hours to seven days (federation.PipeEventResultLifetime). A destination re-derives that window from the signed proof, still admits the legacy 24-hour window, and a destination that predates the longer one is answered by one downgraded retry at the old window instead of a terminal failure — so replies keep flowing while peers upgrade at their own pace. Receipt evidence about a message keeps its own separate 24-hour grace.
Replies can no longer be permanently lost to a local retention re-stamp. The startup migration that extends durable canonical sends matched every pending msg-% outbox row, including the receiver-local msg-fed-… id of an imported message — whose outbox row is a reply. It re-stamped those replies to a +100-year lifetime, which the destination refuses as an invalid proof, and because the same column is the retry deadline it also removed the give-up path, so the reply retried until it happened to reach the peer and collect the permanent 400. The rescue is now scoped to sends, stamps the exact durable sentinel (+36500 days, not SQLite's calendar +100 years), repairs rows an earlier build already extended, and reply envelopes are built from the signed proof so local retention state can never reach the wire.
A refused proof now says why. The destination logged nothing when it refused a proof, and the sender only recorded the destination's single opaque invalid pipeline agent proof refusal, which is how ten historical reply failures stayed unattributable. The destination now logs the exact reason and the sender checks its own reply envelope against that same rule before pushing.
No consensus change or chain migration; app-v27 remains the ceiling.
An agent's working state can now be stored encrypted on the node. Two new surfaces are reachable only from inside the app-v23 pipeline agent boundary, and both refuse to run without the Synaptic Ledger vault: PUT/GET /v1/private-media/{uuid} stores immutable JPEG originals with ciphertext-only rows, per-caller actor isolation, quota enforcement and a startup disk-floor probe, and PUT/GET /v1/workflows[/{uuid}] gives an agent an encrypted, actor-bound journal for long-running work with compare-and-swap revisions, strict argument bounds and an opt-out conversation guard. Neither surface can be reached for another agent's rows, and neither writes a plaintext copy to the database or its WAL.
You can now prove whether message storage is encrypted.GET /v1/messages/storage reports the honest storage posture of this node, and POST /v1/messages accepts a strict signed require_encrypted_storage boolean: an agent that must not be stored in the clear now gets a 503 instead of a silent downgrade, and a malformed or unsigned value is rejected rather than ignored. The authenticated request-body limit became route-aware for exactly one route: a canonical-UUID private-media PUT gets one extra MiB, everything else keeps the 1 MiB ceiling.
Local durability is no longer taken on faith. SQLite opens with synchronous=FULL in both DSN and PRAGMA form and the node verifies journal_mode and synchronous at boot, refusing to serve when the durability posture is not provable. Vault publication is atomic in the same vein: attaching a vault and marking encryption required can no longer be observed separately, so an unlock cannot leave a window where a write is accepted against a store that does not yet require encryption.
Lantern bring-up support.sage-gui init-lantern-private creates a fresh-only hardware identity — it refuses an existing or mismatched node rather than reusing it, takes its companion-key bootstrap explicitly, and never installs services or enables public enrollment. A SAGE_LANTERN_PRIVATE_LISTENERS node treats a missing config as an error instead of a default, and the policy is re-checked when the YAML is loaded so an edit cannot silently weaken it.
A public-memory Merkle index ships dormant, and no fork is opened. The sparse SHA-256 index over committed PUBLIC=0 records, its migration builder and its stage/promote path are in the tree with their tests, and no production code calls any of them. Staged rows live under a local namespace that is excluded from the AppHash; promotion is what writes into AppHash-covered state, it is explicitly named for app-v28, and it is not reachable from a running node. Read that as preparation, not as activation.
Federation: you decide which of your agents the other side can find. A trusted link advertised every eligible ordinary agent to the peer, which is convenient with one agent and confusing with a dozen — the other operator sees names they do not recognise and sends work to the wrong one. Each connection now carries an explicit discovery policy: All agents (the default), Only the ones I pick, or None. Ticking one agent narrows the connection to exactly that agent, ticking more adds them, and Save discovery policy commits it under the same revision-bound agreement the rest of federation uses. It governs listing and exact-name search only: it grants no memory Read and authorises no delivery, and an agent that still refuses federated delivery stays visible as Not accepting by design so the peer is never promised a route it cannot use.
No consensus change or chain migration; app-v27 remains the ceiling.
The Go build floor moves to patched 1.26.8. Both modules now declare go 1.26.8, up from 1.25.13, and every Go container builder moved with them: Dockerfile, deploy/Dockerfile.abci, deploy/Dockerfile.node, both federation-acceptance Dockerfiles and deploy/init-testnet.sh. Building from source now requires Go 1.26.8 or later.
This is not a fix for a hole you have. v11.19.21 and everything before it were built with Go 1.25.13, and that toolchain scans clean on its own standard library. It is a deliberate move forward: the dependency group below requires Go 1.26, and the bare 1.26.0 those tools would otherwise have pinned is precisely the version govulncheck reports 26 reachable standard-library advisories against — among them net/url (GO-2026-6218), html/template (GO-2026-6091), crypto/tls (GO-2026-6090, GO-2026-5856), net/http (GO-2026-6089, GO-2026-5026), encoding/xml (GO-2026-6088), encoding/asn1 (GO-2026-5972), net/textproto (GO-2026-5039) and crypto/x509 (GO-2026-5037). 1.26.6 is the minimum fix for those; 1.26.8 is the newest patch of the line, and on it the vulnerability gate reports no reachable vulnerabilities in either module.
Dependencies refreshed.github.com/jackc/pgx/v5 v5.11.0, github.com/klauspost/compress v1.20.0, golang.org/x/crypto v0.57.0, golang.org/x/sync v0.23.0, golang.org/x/sys v0.48.0, golang.org/x/tools v0.50.0 and modernc.org/sqlite v1.58.0, plus the x/net, x/mod, x/text and x/telemetry indirects. The SQLite driver moves to SQLite 3.53.4, which carries upstream's own journal-rollback fix — the reason the local super-journal patch existed — so that patch retires with no change to recovery behaviour. The pgx bump adds TypeMap to the pgx.Rows interface, so the store tests move from pgxmock/v4 to pgxmock/v5; that is a test-only import change with no runtime effect.
Also in this release: golang.org/x/crypto still carries GO-2026-5932 at v0.57.0, its newest release. It is in the module graph and is not reachable from SAGE code; the gate reports it as uncalled rather than failing.
No consensus change or chain migration; app-v27 remains the ceiling.
A co-commit can no longer re-commit bytes the quorum already rejected. A co-commit commits on block inclusion and never runs the voter, so the content-hash dedup that keeps a rejected memory's exact bytes out was skipped on that one write path: a jointly-signed envelope could re-admit content that had been deprecated, under a fresh memory id, while the same bytes submitted through POST /v1/memory/submit were refused as a duplicate. POST /v1/cocommit/submit now consults the same lookup before it broadcasts and refuses a tombstoned hash with 409 Tombstoned content; the envelope's own SharedID is excluded so an idempotent re-send still works. It is a submission-boundary check rather than a consensus rule — the consensus path deliberately reads no off-chain state — so a node that broadcasts a co-commit transaction directly, bypassing its own REST surface, is not covered by it.
The MCP client stopped keeping its own duplicate rule.sage_remember, sage_observe and sage_reflect used to drop a write when more than 60% of its significant words appeared inside one of the first 50 committed memories in the domain — silently, order-dependently, and only on MCP, so the same write over REST or the SDK landed. They now report the node's own verdict: POST /v1/memory/pre-validate runs the same dedup, quality and consistency checks the vote applies, an exact duplicate comes back as status: "skipped" carrying the node's reason, and a memory that merely shares vocabulary with an existing one is stored instead of discarded.
Also in this release: the validated status is documented as declared-but-unwritten (nothing has ever produced it, and recall would hide such a row), an unused ValidateMemoryRecord that duplicated the REST validator is gone, and the reference docs state plainly that knowledge triples and access_logs are write-only.
No consensus change or chain migration; app-v27 remains the ceiling.
Dedup rejection is sticky. Content that was rejected, challenged, or forgotten can no longer be re-admitted by submitting the identical bytes again: the voter's dedup lookup now matches any other memory that has left proposed, not just committed ones, while a candidate can never match its own row (the v10.1 self-match fix stays fixed). Two identical submissions racing each other no longer veto each other, and a correction still passes whenever its content actually changed.
The dedup lookup — evaluated once per pending memory on the voter's two-second poll — is now indexed on both stores. SQLite gains a content_hash index; Postgres drops its legacy committed-only partial index at startup and rebuilds once, which can make the first boot after upgrade slower on a large memories table.
Also in this release: the README, the sage-memory skill, and the reference docs qualify the BFT/consensus claims for single-validator personal installs, and the papers section now cites the true published provenance.
No consensus change or chain migration; app-v27 remains the ceiling.
Security dependency update: upgrades gRPC-Go from v1.83.1 to v1.83.2, addressing CVE-2026-84445 / GHSA-2v4p-qf9q-27wj. The upstream fix rejects HTTP/2 requests missing both :authority and Host headers, preventing a panic in xDS servers. SAGE's CometBFT servers use ordinary gRPC servers, but the dependency is patched for defense in depth.
No consensus or storage migration; app-v27 remains the ceiling.
Federation agents now visibly orbit their nodes. Motion continues over empty map space and resumes after pointer selection; hovering an agent, keyboard inspection, and dragging keep targets steady. Pause motion and reduced-motion preferences remain supported.
See your federation. CEREBRUM opens connected nodes as an interactive
connectome with agent clusters, search, zoom, a List view, and a selection panel
for exact addresses and connection controls. Gentle ambient agent drift includes
a pause toggle, stops during interaction, and respects reduced-motion settings.
Actual node names make the viewed
node clear, including when you open another SAGE through a tunnel.
A dedicated operator-only SSE stream shows recent message and reply transport
status without exposing message text or proofs. Live changes animate when their
endpoints are loaded; reconnecting refreshes history without replaying old
traffic. The view is bounded, with explicit agent and node paging.
Federation onboarding now explains Exchange codes → Verify together → Explore
agents. Both confirmation screens preserve the explicit number check and
explain that memory sharing is optional. Read, Copy, and Clear domain permissions accept
bulk selection or drag-and-drop into a draft, with an explicit save. Removing
trust keeps its separate confirmation and pairing-again explanation.
No consensus-rule or application-version change; app-v27 remains the ceiling.
Existing permissions and trust agreements stay in place.
Container: ghcr.io/l33tdawg/sage:11.19.17. SDK 11.19.17.
What's New in v11.19.16
Connect nodes, find agents, send messages. Trusted peers running v11.19.16
make eligible ordinary agents discoverable and messageable automatically, without
exporting each agent or granting access to memory domains. Root identities stay
excluded, and explicit messaging blocks still apply.
CEREBRUM adds a searchable directory grouped by node, exact-address copying, and
paged agent lists. Bulk selection and drag-and-drop prepare Read/Copy sharing
choices; saving those choices explicitly grants memory access. Pairing alone
shares no memory domains, and existing approved grants remain in place.
MCP sage_directory searches local and federated agents by default. Upgrade both
peers for automatic node messaging; older peers retain their export-based behavior.
New sends refresh legacy recipient tickets, while queued messages retain their
original authorization mode.
Federated replies now accept the signed claimant-session field emitted by MCP,
fixing peer rejection of otherwise valid replies. Reply retries report the actual
retained delivery state and diagnostic instead of always claiming "queued".
Existing failed events remain failed; the upgrade does not silently resend them.
No consensus-rule or application-version change; app-v27 remains the ceiling.
Container: ghcr.io/l33tdawg/sage:11.19.16. SDK 11.19.16.
What's New in v11.19.15
Consensus-safe memory cleanup, without the 500-record cap. CEREBRUM now
scans the full inventory, previews verified eligible counts, and processes
manual or automatic cleanup through existing consensus challenge transactions.
Open tasks and internal records are protected. The UI distinguishes queued work,
confirmed submissions, and observed outcomes instead of reporting premature success.
Automatic cleanup requires fresh current-Root authorization after upgrading;
old enabled toggles do not silently activate it. Preview does not enable cleanup.
Exact signed transactions are saved before submission for safe recovery. A
challenge may need further votes; audit history is retained. See the
cleanup guide.
No consensus-rule or application-version change; app-v27 remains the ceiling.
Container: ghcr.io/l33tdawg/sage:11.19.15. SDK 11.19.15.
What's New in v11.19.14
Security dependency update: gRPC-Go is upgraded to v1.83.1 to address
HTTP/2 DATA-frame fragmentation heap exhaustion (CVE-2026-84304, Dependabot
alert #45). The required genproto and OpenTelemetry dependencies are refreshed
alongside it. CodeQL workflow actions are pinned to the verified v4.37.9 commit.
This patch introduces no consensus-rule, AppHash-input, key-encoding, fork-target,
or application-version changes. App-v27 remains the supported ceiling.
The stdio MCP bridge no longer self-installs project hooks into the user’s
home directory. When sage-gui mcp starts with $HOME as its working
directory, automatic project repair now returns without writing .claude
hooks or project-relative hook registrations into user-global configuration.
Explicit sage-gui mcp install and sage-gui codex install commands keep their
existing home-directory refusal. Normal project-directory self-healing also
remains unchanged, including when CLAUDE_CONFIG_DIR points elsewhere.
This patch changes no consensus rule, AppHash input, key encoding, fork target,
or application version. Existing app-v27 chains replay byte-identically.
The latest release notes are above. Earlier entries below describe behavior at
their release dates; use the current reference for present-day contracts.
Earlier releases — preserved changelog
What's New in v11.19.12
Project-scoped MCP and Codex installs can no longer corrupt user-global host
configuration.sage-gui mcp install and sage-gui codex install now refuse
to run when the working directory resolves to the user's home directory. Run
the command from the intended project instead; ordinary project installs are
unchanged.
The native-shell build also refreshes its fail-closed checksum for the official
September linuxdeploy-plugin-appimage rebuild. The replacement was produced
by the upstream project's successful scheduled workflow from its unchanged
source commit, and its downloaded SHA-256 matches GitHub's release-asset
digest. An unexpected future replacement will continue to stop the build.
This patch changes no consensus rule, AppHash input, key encoding, fork target,
or application version. Existing app-v27 chains replay byte-identically.
CEREBRUM now supports operator-configured hostnames behind a local TLS
reverse proxy. Set SAGE_ALLOWED_CEREBRUM_HOSTS to an exact comma-separated
hostname allowlist when Caddy, Traefik, or another loopback proxy preserves the
browser-facing Host instead of rewriting it to localhost. Ports are
normalized and wildcards are deliberately unsupported.
The trust boundary stays local: the connected peer and every forwarded IP hop
must still be loopback, unconfigured hostnames still fail closed, and browser
origin matching accepts X-Forwarded-Proto only when every field-line and
comma-joined token is a valid, case-insensitive http or https value and all
hops agree. Empty, malformed, or mixed scheme chains are rejected.
This patch changes no consensus rule, AppHash input, key encoding, fork target,
or application version. Existing app-v27 chains replay byte-identically.
Returning agents can be reviewed normally again. When app-v26 retirement
has handed an agent's former home domain to the stable Root principal,
CEREBRUM reapproval now binds the existing owner and uses the established
Root-to-agent recovery transfer for that exact recorded home. Fresh or
operator-entered domains never receive an implicit transfer.
Rejecting a pending registration now counts active memories—the same lifecycle
view shown by the recovery panel—instead of treating deprecated audit history
as work the operator can still remediate. Active records continue to block
ordinary rejection unless they are deprecated, transferred, or the explicit
attribution-preserving force path is chosen.
This patch changes no consensus rule, AppHash input, key encoding, fork target,
or application version. Existing app-v27 chains replay byte-identically.
Codex workspace identity resolution now fails closed at the filesystem
root. A user-level Codex MCP process launched from / can no longer reuse the
retired global-codex signer or auto-register the synthetic name codex//.
SAGE rejects that broad, untrustworthy boundary before Git discovery,
project-config lookup, key loading, or key generation. Real project and linked
worktree roots continue to resolve to their stable workspace identities;
operators who intentionally need a non-workspace shared identity must pin it
explicitly with SAGE_IDENTITY_PATH.
This patch changes no transaction, AppHash input, key encoding, fork target, or
application version. Existing app-v27 chains replay byte-identically.
Access Groups now discover transferred historical domains, not only each
member's enrollment-time home domain. CEREBRUM's bounded caller-domain
projection consults the consensus-maintained current-owner index for the caller
and active local group peers. A transferred user-* domain therefore appears
as a usable exact recall or write target even when its current owner never
authored a memory there.
Every discovered candidate is still re-authorized against current ownership,
group authority, profile restrictions, and hard denies before it is returned.
Per-record classification checks remain on the memory disclosure path. The
result remains bounded and explicitly reports truncation; it does
not expose a global domain roster, change ownership, copy grants, or weaken
shared-domain and foreign-write restrictions.
This patch changes no transaction, AppHash input, key encoding, fork target, or
application version. Existing app-v27 chains replay byte-identically.
Claim recovery and host wake coordination now survive real multi-transport
runtimes. Both exact-local compatibility claim paths—GET /v1/pipe/inbox
and explicit PUT /v1/pipe/{pipe_id}/claim—atomically bind the session and
create its receipt, so ownership cannot commit without recovery evidence. MCP
claimant identities are durable and transport-scoped across stdio,
Streamable HTTP, and SSE; claimant_identity_mode discloses whether the
identity is durable, a safe concurrent ephemeral fallback, inherited, or
unavailable.
Claim transfer remains deliberate. sage_message_handoff requires the exact
claimant_session_id and claim_revision returned by passive history; stale
or A→B→A delayed transfers fail the revisioned compare-and-swap fence. The
direct REST route preserves pre-v11.19.5 clients by treating an omitted
from_revision as 0 only, so it can move an untouched first-generation claim
but safely conflicts after any transfer. SAGE never steals a claim merely
because it is old.
The new signed, payload-free GET /v1/inbox/activity-state returns exactly
{version,epoch,seq} so host hooks can notice fresh task assignments and
replies. The opaque 32-character database-incarnation epoch survives process
restarts and backup restore, but changes for a fresh database so an old host
cursor cannot suppress new cues after reinitialization.
Those events remain nonblocking coordination: they do not change the exact
three-field {version,seq,pending} contract of /v1/messages/wake or
/v1/messages/wake-state, and they never make Stop treat a reply as unfinished
work. Hooks can surface activity on the next prompt, but cannot resurrect an
already-idle host task.
This patch changes no consensus rule, AppHash input, transaction type, key
encoding, fork target, or application version. The supported consensus ceiling
remains app-v27.
Updater governance compatibility and recovery state are now one atomic
proof. The replacement binary reports its own maximum supported application
version. SAGE validates canonical pending-plan and active-ballot state against
that exact ceiling while holding the same runtime read fence that pins the
snapshot height and AppHash. Consensus cannot publish newer governance state
between the compatibility decision and the verified recovery snapshot.
v11.19.3 acquired those two read fences separately. Its snapshot was coherent,
but a concurrent Commit could make the preceding compatibility verdict stale.
Personal single-node installs still upgrade normally in the app: v11.19.3 and
v11.19.4 have the same app-v27 ceiling, the personal-node watchdog cannot create
an unsupported app-v28 transition, and the updater performs the recovery
snapshot, coordinated stop, final stopped-state snapshot, install, rollback,
and restart automatically. No CLI or manual preflight is required. The
stopped-node procedure in docs/UPGRADING.md is only for
quorum or externally managed nodes where an operator can mutate governance
during the v11.19.3 check-to-fence window.
This patch changes no consensus rule, AppHash input, transaction type, key
encoding, fork target, or application version. Existing app-v27 chains replay
byte-identically.
Normal upgrades now preserve compatible governance state automatically.
The desktop updater reads the canonical pending plan and active proposal under
one runtime-consistent view before changing the executable. A supported
in-flight upgrade is included in the existing verified recovery snapshot and
continues after restart; it is not a reason to interrupt the user or block the
update. No terminal command, preflight ceremony, or governance expertise is
required.
Malformed canonical state, an undecodable upgrade ballot, or a target newer
than this binary supports still fails closed before executable mutation. The
technical upgrade status and stopped-node upgrade preflight commands remain
available for headless and quorum operators. Superseded safety notice: the
v11.19.3 live updater did not hold one uninterrupted fence across that check and
snapshot capture. That does not impose a CLI step on a personal node; only
quorum or externally managed governance needs the coordinated stopped-node
procedure when leaving v11.19.3.
This patch changes no consensus rule, AppHash input, transaction type, key
encoding, fork target, or application version. Existing app-v27 chains replay
byte-identically.
Binary replacement now has consensus-authoritative upgrade-governance
proof. The read-only /upgrade/governance-status ABCI query reports the
current application version, the exact pending upgrade:plan record, and the
canonical state:gov:active proposal. Upgrade ballots include their decoded
target application version. Storage, pointer/proposal identity, bounds,
canonical-name, status, height, and payload-decode failures return ABCI code 1
instead of being misreported as an empty plan or ballot.
sage-gui upgrade status now consumes that fail-closed query rather than
inferring safety from /abci_info plus the off-chain dashboard projection. The
stopped-node sage-gui upgrade preflight command uses the same canonical
inspector before the new server starts. v11.19.3 integrates that compatibility
decision into the normal updater and lets supported in-flight operations
continue automatically.
This patch changes no consensus rule, AppHash input, transaction type, key
encoding, fork target, or application version. Existing app-v27 chains replay
byte-identically.
Stranded message claims remain recoverable beyond the retained-history
window.sage_inbox now embeds the first passive, payload-free page of
unfinished claims held by another runtime sharing the same exact agent identity.
Agents can continue through every older page with
sage_message_history(folder="claimed_elsewhere"), then deliberately transfer
an exact claim through the existing compare-and-swap sage_message_handoff
path after deciding that its former claimant is dead or stale.
The recovery projection exposes only the message ID, claimant-session fence,
timestamps, and local/federated classification needed for safe handoff. It does
not expose sender identity, intent, payload, result, provider, or chain IDs.
Expired TTL-bounded claims are also excluded consistently from both the exact
diagnostic count and its recovery pages before the periodic expiry sweep runs.
Provider-addressed compatibility messages now bind atomically to the exact
claiming agent and MCP session, resurface on later polls, support CAS handoff,
and complete idempotently through sage_message_reply. A failed reply explicitly
does not authorize creating a substitute sage_message_send; agents must recover
the original claim or report the failure. Existing claimed provider rows receive
an off-chain SQLite legacy session fence during startup migration.
This patch introduces no consensus change or application-version increase. The
supported consensus ceiling remains app-v27.
Record authors regain lifecycle authority in reserved shared namespaces.
After app-v27 activates, the immutable author of a record in general, self,
meta, or sage-* may challenge that record and may reinstate its open
challenge without separately holding a level-3 Modify grant. The exception is
record-scoped and does not apply to governance-promoted shared domains. Pending
or inactive enrollment, read-only/profile restrictions, shared-write denies,
and classification/clearance failures still deny the action. App-v21 weighted
challenge rounds include the eligible record author in their frozen electorate.
Omitted task status now has one canonical meaning. After app-v27, a signed
new-task request that omits task_status is canonicalized to planned by both
REST transaction construction and consensus proof verification. Pre-app-v27
chains retain the historical requirement to send task_status: "planned"
explicitly, preserving replay and AppHash compatibility.
App-v27 is a governed consensus upgrade from app-v26 with no state migration.
Its rules begin at H+1 after activation; older blocks replay under their original
application version.
Reserved shared domains are readable again without becoming ownable. Active
local principals can read the compile-time shared namespaces general, self,
meta, and sage-*, while each record's classification still applies. This
restores the shared-domain behavior expected by existing agents without opening
private or restricted records.
Access-grant transactions now reject attempts to register either those reserved
namespaces or governance-promoted shared domains as owned domains. REST reports
that conflict as a forbidden request, and the API, SDK, and RBAC references now
state the same contract.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.28 introduces no app-v27.
Empty semantic recall now distinguishes genuine absence from an incomplete
vector-space view. For an empty, domain-scoped semantic query, index_status
reports complete, incomplete, or fail-closed unavailable only when the
caller and exact query universe support that conclusion. The same signal is
relayed through sage_recall and sage_turn, so write-on-absence agents can
avoid manufacturing duplicates when committed memories are temporarily
unreachable in the active embedding space.
The proof is caller-safe and race-fenced across canonical projection, SQL,
embedding-space, and vault generations. Narrowed or federated queries and
unhealthy projections never receive a false completeness claim, while bounded
indexed probes keep the empty-result path operationally safe.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.27 introduces no app-v27.
The supported Go dependency baseline is refreshed. This release carries
the validated testify 1.12.0, x/crypto 0.55.0, and x/tools 0.49.0 module
updates already exercised by the full repository gate.
Code scanning and native-shell CI actions are refreshed to their pinned
current revisions. CodeQL runs with the updated action bundle and the native
shell cache action is updated, without changing SAGE runtime behavior.
HTTP MCP tokens now bind to existing approved managed identities. On
app-v23 nodes, token creation no longer generates an unapprovable pending
principal: Root/Admin selects an active ordinary agent already managed by the
node, and issuance fails closed if its exact key is unavailable. The CLI also
handles mcp-token create --help without minting a credential and rejects
unknown creation flags.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.26 introduces no app-v27.
Federated inbox work now has recoverable, session-fenced ownership. SAGE
binds an inbound federated claim to the receiving MCP session before exposing
its payload. Retained older claims receive an explicit legacy CAS fence for
deliberate handoff; live work is never stolen by a timeout. Reply completion,
the claimant check, encrypted result fingerprint, and durable return event now
commit atomically, so a lost-response retry returns the original event while a
different second reply conflicts.
MCP boot guidance no longer rides inside ordinary tool results. Lifecycle
standing stays in initialize.instructions, including for a client that
initializes after its first tool call. The former imperative block that asked an
agent to invoke tools and edit a memory file has been removed, keeping the inbox
trust boundary internally consistent.
Embedding-space and retention diagnostics are more precise. The readiness
guard labels only a qualified-versus-bare spelling of the same provider/model
leaf/dimension as a likely alias, without collapsing two organizations that
publish the same basename. Durable-until-handled presentation is limited to
actionable pending/claimed work, while mixed-version retention-only responses
remain compatible.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.24 introduces no app-v27.
Turn-time recall now carries the same trust and lifecycle evidence as explicit
recall.sage_turn includes each recalled memory's corroboration_count and
status, so the every-turn path no longer hides corroboration weight or whether
a recalled row is committed or currently challenged.
Embedding-space drift is visible before it silently empties semantic recall.
At boot, SAGE compares the active embedder with the non-deprecated vector spaces
already in the local store. A mismatch produces a loud warning and a structured
embedding_space block in /ready; the node remains available by default while
strict readiness returns 503, allowing an intentional re-embed migration to
finish instead of turning a quality warning into an outage.
Managed reranker setup now diagnoses incompatible prebuilt engines. After a
verified install, SAGE preflights llama-server. Proven GLIBC, GLIBCXX, or CXXABI
loader failures preserve the loader's real error and point operators to the
bring-your-own reranker path instead of reporting a successful unusable install.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.23 introduces no app-v27.
A missing optional ForceGraph API can no longer strand CEREBRUM after the
verified brain has rendered. The renderer now publishes the core graph and
truthful counts before optional anatomical, control, and interaction setup. The
bundled runtime's absent clickAfterDrag helper is feature-gated, so the brain
hull, controls, and auto-rotation continue instead of falling into the cold
unavailable path with real nodes already on screen.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.22 introduces no app-v27.
A domain-summary refresh can no longer cover a verified MRI graph. The MRI
renderer is now the sole authority for the central unavailable overlay. Domain
inventory failures stay localized to their own retrying panel, while genuine
cold graph failures and unsafe mode switches remain fail-closed.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.21 introduces no app-v27.
A transient MRI refresh no longer hides a graph CEREBRUM has already
verified. Memory and Connectome snapshots now retain their explicit source
mode. If a live refresh fails, CEREBRUM keeps the last verified snapshot visible
only when it belongs to that same mode, while retrying in the background. Cold
failures and failed mode switches still fail closed, so Connectome bytes can
never masquerade as a verified memory projection.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.20 introduces no app-v27.
Codex project hooks stay inside their project. The v11.18.18 byte-exact
self-healer could mistake Codex's user-global ~/.codex configuration directory
for a project and generate a global hooks.json. That made an unrelated Codex
task receive SAGE inbox Stop nudges for the shared agent identity. The healer now
refuses the user-home/global scope; project-local hooks continue to self-repair.
Connectome clicks now have one hit-tested owner. The redundant DOM click
fallback that raced ForceGraph's deferred node click is gone. Small pointer
wobble is handled by one explicit tolerance, background dismissal uses the
graph's raycast result, and clicking a second neuron no longer closes the
inspector and starts a competing zoom-out first. Raw domain-access metadata is
summarized behind a bounded disclosure below traffic, relationships, and memory
details; bloomed memory nodes now expose hover and accessible click feedback.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.19 introduces no app-v27.
Codex upgrades now repair stale SAGE lifecycle hooks automatically. On
every MCP startup, the project self-healer compares all five installer-owned
Codex hook scripts with their fully rendered current templates. A mixed
generation can no longer pass merely because the files exist or another hook
mentions the current binary. Upgrading therefore replaces legacy no-op Stop
hooks and malformed prompt hooks without requiring a second manual
sage-gui codex install run.
The CEREBRUM Connectome now leads with the graph itself. Neurons have a
larger practical click target; clicking one opens its persistent identity,
visible incoming/outgoing traffic, strongest peer, directed connection list,
and visible memory lobe. The compact fallback selector now shows only agents
with visible peer relationships, ordered by retained traffic, instead of
turning a large dormant/test roster into the primary navigation surface.
Isolated authorized neurons remain visible and clickable in the brain and join
the selector while selected.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.18 introduces no app-v27.
Routine MCP restarts no longer make the same stdio agent disown its own
unfinished messages. The primary stdio runtime now persists one opaque
claimant identity per exact signed agent, provider, and project under
SAGE_HOME, and holds an OS advisory lock as the liveness fence. A later
runtime reuses that identity only after the earlier process is no longer live;
a genuinely concurrent runtime receives an independent identity and retains
the existing one-handler and compare-and-swap handoff boundary. In-place
installed-binary handoff also carries the current claimant identity while the
old process keeps the lock alive.
The fix is deliberately prospective and does not bulk-transfer historical
claims created by pre-v11.18.17 random process identities. Those rows remain
visible through claimed_elsewhere_count and passive history and can still be
transferred one at a time with the existing CAS-fenced handoff after the old
claimant is known dead. HTTP MCP conversations remain transport-scoped. This
patch introduces no new consensus application version or state migration. The
ceiling remains app-v26; v11.18.17 introduces no app-v27.
Claimed agent work no longer disappears from the inbox that claimed it.sage_inbox now returns a separate bounded own_claimed_unfinished projection
for messages already owned by the exact current agent session. The projection
is passive: it never claims, refreshes, transfers, or duplicates work, and it
does not change the established items or count meaning of newly available
work. Exact agent/session filtering, completion and expiry handling, bounded
results with an exact total, reply-after-repoll, and nonmutation are pinned by
store, REST, and MCP regression coverage.
The payload-free hook status path also checks the durable wake snapshot, so
claimed-but-unfinished work cannot be reported as a clean inbox merely because
no unclaimed row remains. Older or temporarily incapable nodes degrade to an
explicit unavailable state instead of either a false zero or a failed primary
inbox call. This patch does not automatically transfer claims from another
session; passive history plus explicit compare-and-swap handoff remain the
recovery boundary. It introduces no new consensus application version or state
migration. The ceiling remains app-v26; v11.18.16 introduces no app-v27.
Every unfinished exact-recipient local canonical message now has a durable
wake generation, including upgrade-era claimed-only work and sends through the
deprecated pipe route. Startup backfill covers both pending and claimed rows, so a recipient
whose only live work was already claimed cannot reopen as the silent
{seq:0,pending:true} state. Keyed exact-local pipe sends use the canonical
idempotent admission path; unkeyed sends insert the row and advance the same
recipient sequence atomically. Publication happens only after commit, exact
replays do not republish, and an incapable backend fails before insertion rather
than creating durable work that wake consumers cannot observe as new.
The experimental Claude notification adapter is explicitly opt-in again. The
shipped Claude Code host registers the custom notification handler, but
end-to-end delivery from a plain .mcp.json server through its plugin-scoped
gate remains unverified. An idle adapter would also acquire the one exact-agent
wake lease and exclude a useful long-running consumer.
SAGE_CLAUDE_CHANNEL=true enables it for an operator who has confirmed that
delivery path.
Pending-memory presentation is deterministic even when creation timestamps tie:
SQLite and PostgreSQL both use memory_id as the final ordering key. The
documentation citation guard now parses newline-separated and hyphenated paths,
pins every concrete declaration/lead/interior anchor, repairs only explicitly
accepted declaration anchors, refuses semantic locations it cannot reconstruct,
and inventories the remaining legacy skipped and bare references. This patch
introduces no new consensus application version or state migration. The ceiling
remains app-v26; v11.18.15 introduces no app-v27.
Durable agent messages now stay visible until the work is actually
finished. Claiming a message no longer makes the payload-free wake surface go
quiet: both pending and claimed rows remain unfinished, a reconnect at the
current cursor receives an immediate wake, and sage_inbox reports an exact
payload-free claimed-elsewhere state instead of a bounded-scan false zero.
Claude Code project sessions arm the signed wake channel by default, while the
optional Stop hook reads a lease-free monotonic snapshot so new or stranded
work can nudge a session once without stealing the live SSE consumer lease.
The same recovery path is honest at its edges. A claimant-session fence
rejection remains a typed conflict instead of masquerading as a missing
message, and history plus explicit compare-and-swap handoff remain the only
way to recover another session's claim. Canonical retention migration now
rescues only the exact historical 24-hour stamp, preserving a sender's chosen
bounded TTL across every store reopen, including RFC3339 nanosecond timestamps.
CEREBRUM's Connectome now identifies the agents it renders. Hover details
are positioned and escaped reliably, while click, tap, and keyboard selection
open one persistent inspector with exact agent identity, visible retained
traffic, peers, activity, and an independently loading visible-memory lobe.
Selection survives authorized live refreshes, error and empty states stay
truthful, mobile uses a bounded sheet, and reduced-motion and established
Connectome guidance remain intact.
Agent-as-lobe corroborator reads now use one deterministically ordered bounded
batch instead of an N+1 query pattern, with matching SQLite and PostgreSQL
ordering. The MCP contract also states the server-enforced 31-day
sage_timeline range rather than advertising requests the server rejects.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.14 introduces no app-v27.
Hubanov's distributed-engram contribution now connects memories to the
neurons that corroborated them. CEREBRUM keeps agent and memory identities in
separate graph namespaces, rejects stale bloom generations, and removes every
transient bridge on focus or graph replacement. The server uses a deterministic,
indexed 96-row evidence prefix and exposes at most 12 authorized bridges without
turning historical corroboration into a claim of current possession.
Claude's production wake source can now arm the payload-free message bus.
When explicitly enabled with SAGE_CLAUDE_CHANNEL, the MCP runtime consumes the
existing signed SSE wake route with a random process lease and resumable cursor.
Delivery applies backpressure instead of dropping the newest wake, and shutdown
releases saturated readers without leaking goroutines or claiming message
content.
The Connectome no longer floats an instructional card over the brain. Its
guidance lives in the existing reading panel, the mode toggle keeps one stable
name and visible pressed state in both themes, keyboard focus remains clear,
mobile Reset behavior stays intentional, and view changes are announced to
assistive technology.
This patch also closes a claimant-session compatibility bypass: a current typed
404 is authoritative, the deprecated pipe-result alias carries the active MCP
session, and only a genuine old-node route miss may fall back. It introduces no
new consensus application version or state migration. The ceiling remains
app-v26; v11.18.13 introduces no app-v27.
CEREBRUM can now open an agent as a memory lobe. Selecting a connectome
neuron lazily blooms that agent's highest-confidence visible memories as
engrams, while retaining the operator-only route and app-v23 per-record
projection checks. The indexed, bounded query avoids whole-brain scans; stale,
failed, and disposed frontend requests cannot leave another agent's lobe on
screen.
Dashboard live activity is now guarded as one exact 20-event registry. The
seven previously unwired operator events now reach the existing dashboard SSE
stream, while message wake, MCP, and wizard protocols stay route-local. A
fail-closed typed control-flow audit and executable browser contract reject
dead, aliased, escaped, build-tagged, or decoy event sinks.
Signed task creation and message attribution now agree end to end. Every
official task constructor explicitly signs the required initial planned
status, and REST fails fast instead of mutating an omitted signed field into a
transaction that app-v23 through app-v26 must reject. Authorized message and
pipe responses retain exact immutable agent IDs alongside mutable presentation
labels, use one bounded batch metadata query on healthy production stores with
a bounded exact-ID fallback, suppress foreign-chain label collisions, and keep
count-only responses identity-free.
This patch also repairs release-facing documentation drift, pins the current
33-tool MCP inventory, and adds fail-closed symbol/citation coverage for the
references it can verify. It introduces no new consensus application version
or state migration. The ceiling remains app-v26; v11.18.12 introduces no
app-v27.
The CEREBRUM connectome now fires live without widening its operator-only
boundary. Successful local message sends emit a contentless invalidation tick;
the browser then refetches the existing caller-filtered snapshot and pulses only
newly observed synapses. Monotonic generations preserve later ticks across
in-flight requests, ordinary reloads, failures, and retries, while initial loads
and unrelated refreshes never create false activity.
Dashboard retrieval activity no longer duplicates authorized memory plaintext
into the global operator stream. Recall, search, and hybrid events now expose
only their event type and result count. The obsolete expandable plaintext panel
is gone, and serialized-frame regressions pin the contentless contract and live,
non-replayed delivery.
Claude bookend sessions can discover waiting SAGE messages without claiming or
revealing them. A signed, payload-free inbox-status hook reports only the
current identity and unread count, makes failures visible, preserves unrelated
user hooks during self-heal, and exposes the read-only message tools needed to
perform the explicit inbox fetch.
This patch also keys local connectome locality by chain identity, removes a stale
app-v7 validator warning after app-v14, dims the connectome skull for legibility,
requires patched Go 1.25.13 throughout current builders and CI, and publishes
checksum sidecars for Windows executables. It introduces no new consensus
application version or state migration. The ceiling remains app-v26;
v11.18.11 introduces no app-v27.
Multiple MCP runtimes sharing one agent identity can no longer silently lose
track of claimed messages. Every MCP conversation now has an opaque claimant
session ID. Atomic inbox claims persist that session in passive history, an
explicit compare-and-swap handoff transfers work between runtimes, and a stale
former owner is rejected if it tries to reply after ownership moved. Receive
tokens remain replay-safe after a lost response, while legacy direct REST
clients retain their existing agent-level compatibility path.
CEREBRUM can render the agent message bus as a live connectome inside the 3D
brain. Registered agents become domain-coloured neurons, directed channels
become traffic-weighted synapses, and hub agents settle toward the core. The
view consumes the existing RBAC-filtered synapse projection, drops ghost edges,
and fences asynchronous mode switches so a slow memory response can never be
displayed as connectome data.
Upgrade-watchdog submissions can no longer hold a signing key's nonce lease
for the process lifetime when CometBFT wedges. One bounded context now covers
both lease acquisition and the broadcast. A deadline after submission remains
a typed indeterminate outcome, so the exact signer and bytes stay fenced until
their fate is reconciled; elapsed time never releases the key fail-open.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.10 introduces no app-v27.
Ambiguous CometBFT commit and sync outcomes are now typed at the shared
broadcaster boundary. Transport, status, RPC, decode, shape, hash-binding, and
missing-height failures return ErrSubmitIndeterminate for valid signing keys,
while the existing live-registration path remains an independent fence
backstop. Pre-send request-construction failures remain definitive and do not
fence a key over bytes that never reached a transport.
Federation sync now fails closed if its commit broadcaster ever violates its
contract by returning neither a result nor an error. The exact signer and
encoded transaction remain fenced until reconciliation proves their fate,
instead of releasing the key for a potentially in-flight transaction. A new
cross-package decoder contract also pins the HTTP prologue shared by
internal/tx and the CEREBRUM web path while recording their deliberate verdict
and envelope-tolerance differences.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.9 introduces no app-v27.
CometBFT transaction submissions no longer permit Go's HTTP transport to
transparently redeliver a fenced request after a reused connection fails while
reading the response. Commit, sync, byte-identical nonce-fence reconciliation,
and CEREBRUM submission paths now share a non-reusing HTTP/1.1 transport seam.
Each submission call writes its transaction on one connection and returns an
indeterminate result instead of silently delivering the same signed bytes to a
second responder. Restart failure reporting also preserves the signer-fence
veto ahead of a generic drain timeout.
MCP reply polling now fails safe when a caller presents an unsafe forward
watermark. If reply_since is later than the authoritative retained-reply
head, or no head exists to validate it, sage_inbox returns the newest passive
reply page for deduplication instead of filtering a formal reply into a false
empty result. Complete recovered pages become a new safe baseline; truncated
pages require composite-cursor catch-up, and failed page reads never claim
recovery. A successful outbound sage_message_send also performs one bounded,
sender-exact passive inbox snapshot so an inbound message that arrived after an
earlier empty poll is surfaced during continued coordination.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.8 introduces no app-v27.
Large signed transactions now use a bounded CometBFT transport instead of
overflowing request headers. Existing smaller broadcasts keep the established
GET wire shape, while large commit, sync, and byte-identical nonce-fence
reconciliation requests use JSON-RPC POST with base64 transaction bytes. Client
transaction and JSON-RPC body limits are independently range-checked, capped at
8,000,000 bytes, and refuse an oversized request before send. Operators raising
them must configure matching CometBFT limits. Independently, every validator
enforces a 1,200,000-byte aggregate raw-transaction budget for app-v20 atomic
finalization, sufficient for the measured 1,304-entry SkillRegistry transaction.
Memory content remains bounded at 512 KiB, while the canonical signed
AgentRequest proof has its own 600,000-byte consensus bound, admitting the
measured 573,723-byte proof without widening the content or aggregate limits.
Response handling accepts strict quoted or numeric int64 heights, rejects
fractional, exponent, null, malformed, and out-of-range heights, and refuses
unsupported content types.
Federation route refresh no longer risks recursively acquiring the
sync-policy read lease from a peer-request caller. Opportunistic refresh
admission is policy-free and bounded to one pending refresh per peer; the
agreement and binding lookup runs asynchronously after the request caller can
release its lease. Failed-request and successful-Direct triggers remain
covered, while the route-exchange endpoint does not self-trigger refresh.
P2P-only peers can recover when their stored route snapshot is missing or
belongs to an older trust generation. Only the authenticated
/fed/v1/p2p/routes bootstrap exchange may use stale or current route addresses
as connection hints; the current agreement's pinned mTLS identity remains
authoritative. Protected requests reject missing or cross-generation snapshots
with trust_generation_mismatch. A matching-generation empty target set remains
explicitly pinned and cannot fall back to current configuration.
Federation diagnostics now give security evidence precedence over route
availability evidence. Mixed route-availability plus certificate, SPKI, pin,
identity-mismatch, or security-block evidence is classified as
security_blocked; revocation, expired or unknown agreement, trust-failure, or
authentication evidence is classified as trust_failure. Both verdict classes
outrank route availability.
This patch introduces no new consensus application version or state migration.
The ceiling remains app-v26; v11.18.7 introduces no app-v27.
Updater snapshots now prove both supported CometBFT layouts before they are
published or reused. Application Badger and persisted consensus state must
match at height H and agree on the application hash. A blockstore committed
through H is accepted only after its H block ID and seen commit match that
state. If the blockstore is durably one block ahead at H+1, SAGE additionally
verifies the complete block and part identity, direct-parent and state-derived
header fields, last and seen commits, validator signatures, and CometBFT's
replay-time block validation. Regression coverage restores the candidate and
runs the real CometBFT handshaker, proving exactly one replayed block and safe
restart reuse. Malformed or more-than-one-ahead provenance is rejected, and an
invalid prior publication is quarantined before a valid replacement can be
published. Cancellation always blocks executable updater handoff, although a
safe snapshot may already have been atomically published. Non-empty H+1
evidence is retryable until application and state catch up.
Federation Retry now performs one bounded, exact-generation recovery
workflow. Concurrent operator clicks share the same route refresh and
authenticated status probe. Direct and relay targets are frozen to the active
JOIN generation, HTTP 401/403 and certificate/identity failures stop before
re-probing, and a revoke or re-pair during the response invalidates the result.
Typed dashboard diagnostics distinguish missing or expired route bundles,
stale Direct routes, unavailable relays, trust-generation changes, and legacy
connections that must be paired again. Ordinary polling and mutating requests
do not enter this retry path.
Memory-reassignment audit failures no longer place request-controlled agent
IDs in logs. The source and target are represented by fixed 96-bit truncated
SHA-256 fingerprints (24 lowercase hexadecimal characters), preserving stable
incident correlation without allowing CR/LF or other control characters to
forge log records.
This patch does not change consensus state or application activation. The
ceiling remains app-v26; v11.18.6 introduces no app-v27. The signer fence
also remains process-local: unresolved submissions still require proof of fate,
and crash/restart or a separate signing process is not claimed safe until
durable cross-process pre-broadcast intent exists.
Long-lived stdio MCP sessions now follow an installed SAGE upgrade without
executing a request under stale tools. The MCP process snapshots the exact
executable that started it. If the app bundle or binary is atomically replaced,
the next unread JSON-RPC frame is handed to the new executable together with
the remaining stdio stream. The upgraded runtime—not the stale process—receives
that request. The old runtime never executes the handed-off frame; transport
failure remains an ordinary indeterminate outcome for callers to reconcile.
Sessions initialized on 11.18.5 advertise MCP tool-list change support; the
replacement emits notifications/tools/list_changed only after the logical
session has completed initialization, so conforming clients refresh cached
definitions as well as runtime behavior.
The unified coordination response identifies its live contract. Every
sage_inbox result now carries coordination_schema: "sage.inbox.v2", the
running mcp_runtime_version, and sender_replies_embedded: true|false. Monitors can
therefore reject or report a stale pointer-only session instead of silently
assuming that an empty addressed inbox also means no threaded reply arrived.
The existing bounded reply_items, inclusive watermark, composite catch-up
cursor, and passive sender-only authorization remain unchanged.
The upgrade from a pre-11.18.5 MCP process still requires one agent-session
restart because that already-running older process cannot contain this handoff
logic. Once a session starts on 11.18.5 or later, subsequent binary replacements
use the automatic request-preserving handoff. Clients that ignore the negotiated
tool-list notification must still re-list tools or reconnect to discover new
definitions.
The consensus ceiling remains app-v26; v11.18.5 introduces no app-v27.
One inbox poll now surfaces both new work and threaded answers.sage_inbox returns replies to messages you sent under the separate passive
reply_items key by default, while genuine inbound work remains under items.
Reply rows never inflate work counts and explicitly require no reply. Inclusive
reply_since polling prevents same-millisecond loss; truncated pages fail safe
with an exact composite-cursor catch-up action and forbid advancing the
watermark until the window is drained.
Release builders now enforce the patched Go floor. Root and natter
modules require Go 1.25.12, CI and release jobs resolve that exact go.mod
toolchain, every Go container builder uses 1.25.12, and pinned
govulncheck v1.6.0 scans both modules before either CI fan-in or release
publication can pass.
Legacy pipeline retention compares time chronologically and conservatively.
SQLite purge eligibility no longer relies on variable-width RFC3339 text.
Cutoffs are floored to SQLite's millisecond precision, so ambiguous
same-millisecond rows are retained rather than deleted early; malformed read
evidence also retains fail safe.
The consensus ceiling remains app-v26; v11.18.4 introduces no app-v27.
Same-key consensus submissions now fail closed across every producer in the
running daemon. The dashboard, REST API, federation manager, voter, and
upgrade watchdog share a per-key nonce lease. Once exact transaction bytes
reach CometBFT, any unproven transport, status, RPC, decode, shape, hash, or
height outcome fences that signing key until reconciliation proves those same
bytes committed or permanently refused. Strict shared Comet decoders require a
single bounded JSON document, explicit nested verdicts, the exact transaction
hash, and a positive committed height for success.
Update restart advice now follows live fence state. A completed download no
longer leaves stale restart guidance behind: retained update status reads
recompute whether restart is currently safe, and the dashboard renders
the server-provided instructions. Coordinated restarts are refused when a fence
is present. Crash, power-loss, cross-restart, and separate-process CLI exposure
still require durable pre-broadcast intent and remain explicitly out of scope.
The consensus ceiling remains app-v26; v11.18.3 introduces no app-v27.
A reply to a message you sent is readable again, through an advertised MCP
tool. Previously a recipient could answer, the durable row flipped to
completed, and the answer was reachable only through the passive REST
projection GET /v1/pipe/results — which no MCP tool ever called. sage_inbox
shows work addressed to you, not answers to you, and sage_message_status is
sender-only but deliberately payload-free. So in MCP and bookend clients the
reply was invisible and work round-tripped. v11.18.2 adds sage_message_replies
as an explicit sender-side read (SAGE now advertises 32 MCP tools) plus a
payload-free pointer inside sage_inbox that reports how many replies are
retained without ever presenting them as new work.
The reply read is exact-sender-only, passive, and honest about provenance.
Authorization is the SQL predicate from_agent = ? against the caller's own
signed identity — not the wider callerCanViewPipe rule the workflow route
uses — and no parameter names another agent, so the tool cannot serve as a
message-existence oracle. Reading claims nothing, acknowledges nothing, and
re-queues nothing. Every body is labelled untrusted data and attributed to the
agent that actually wrote it rather than the agent you addressed.
GET /v1/pipe/results gains a payload-free ?count_only=1 probe and a
composite (completed_at, pipe_id)before= cursor, so replies sharing a
millisecond are never stranded behind the page boundary. A store backend
lacking the optional capability answers 501 instead of an empty page that
would read as "no replies".
Memory, agent, RBAC, federation, and consensus behavior are unchanged; app-v26
remains the binary ceiling and v11.18.2 introduces no app-v27.
MCP session guidance now uses the protocol surface intended for it. SAGE
runs its per-session boot standing during initialize and returns the adaptive
full, bookend, on-demand, pending-review, or unavailable guidance through
initialize.instructions. The first real tool result is therefore only that
tool's payload instead of being prefixed with a 1.5–2.9 KB auto-connect block.
Repeated or concurrent initialization in one transport session reuses the same
standing without duplicating signed registration or caller-scoped reads.
Clients that skip the MCP initialization handshake keep the one-time first-tool
fallback for compatibility.
Legacy-lineage recovery now represents real skip-ahead history truthfully.
When retained Comet history proves a version jump such as 1→7 or 8→11, the
app-v21 doctor emits a v2 transition claim at the real activation height and
records the skipped predecessors as virtual, subsumed coverage. It never
invents interleaved heights or writes synthetic upgrade:applied:* records.
Every validator independently replays the retained history and hashes before
an explicit vote, and the immutable audit is installed atomically only when
app-v22 activates. Existing valid v1 receipts on already-upgraded app-v22+
chains remain readable; new v1 repair proposals fail closed.
The lineage change is confined to the exceptional app-v21 → app-v22 recovery
ceremony. Memory, agent, RBAC, and federation policy are unchanged; app-v26
remains the binary ceiling and v11.18.1 introduces no app-v27.
What's New in v11.18.0
A connected pair is now the federation group users expect it to be. Each
side explicitly exports the ordinary local agents it places in that pair. Every
active ordinary agent on the other SAGE may then live-read those exported
agents' owned domain trees by default—no matching local group, receiving
domain, or linked-reader grant is required. A receiving operator can narrow
that default with exact agent/domain denials. Local-only group membership is
never exported transitively, and adding another federated agent is an explicit
new export. Read remains borrowed; Copy still requires a source offer plus the
receiver's Save here subscription, while remote memory Write remains
reserved and fails closed.
Federated authorization now stays true through disclosure. The signed Read
plan and single-use challenge bind the exact source-agent standing, clearance,
export, agreement, policy generation, and negotiated authorization model. The
source authorization lease is revalidated and held until the destination query
finishes, so a concurrent rename-safe identity change, restriction, ownership
change, pause, or revoke cannot leak a result. CEREBRUM and sage_federation
also report authenticated-read readiness honestly, and the Docker acceptance
lane proves default Read, explicit denial, non-transitive exports, bidirectional
Copy backfill/incremental sync, and restart recovery.
People can address agents without giving up canonical identity. Local and
federated message targets accept a unique display or immutable registered name;
the resolved request and wire proof still car