Sibyl Memory that outlives the laptop.
Wipe the machine, connect the wallet, and the agent comes back knowing what it knew, and proves it was not tampered with. kint keeps Sibyl Memory wallet-owned, on Base, and checked before the agent acts on it.
What kint is#
kint is a drop-in MCP server for coding agents that keeps Sibyl Memory wallet-owned: encrypted to a key only the owner can produce, written to Base, restored on any machine the owner connects, and checked against the chain before the agent acts on a recalled row.
This page covers what kint does in v0.3.0, the pieces it is made of, what is live on Base mainnet, and how the rest of these docs are laid out.
The bet#
Sibyl decides what the agent remembers. kint decides where that memory lives and who can read it: the wallet.
Sibyl Memory gives a coding agent five tiers, full-text search, eight MCP tools and one SQLite file. That file lives on one machine. kint is built on Sibyl Memory and sits under it: kint-server imports Sibyl's MCP server untouched (build_server() from sibyl_memory_mcp.server), keeps their eight tools unmodified, and adds six of its own. The agent keeps writing memory the way it always did. kint takes the rows that changed since the last anchored epoch, compresses them, pads them to a size bucket, encrypts them and appends them to a chain of epochs on Base.
s0nderlabs runs no server in that path. The pieces are your machine, the owner wallet, a small contract on Base with no admin, and the RPC endpoints you read it through (KINT_RPC_URL and KINT_RPC_URL_2 when you set them; otherwise public Base RPC endpoints, or an Alchemy key when the macOS Keychain holds one as dev.api.alchemy).
Take Sibyl away and nothing is left to decide on. The decision step reaches the row through multi_record_search, the same gated search Sibyl's memory_search runs, and keys on its typed verdict. Without Sibyl an epoch is a decrypted blob with no query surface. Base holds the ciphertext; Sibyl is what turns it back into a store the agent can ask. Built on Sibyl Memory walks through this with file and line.
What it does today#
Restore by wallet connect. On a new or wiped machine, kint connect recovers the data key: an EOA owner signs one frozen EIP-712 message, a Base Account owner types the vault passphrase, and a recovery code covers the day both are gone. kint pull then walks the epochs back from the head. It checks each ciphertext's keccak against the digest in the contract's Epoch event and opens it under an AAD that binds chain id, owner, space, seq, prev, bucket, rows_root and dek_id. Each row is replayed through Sibyl's own write methods (set_entity, set_state, set_reference, write_event), then re-read and compared. A cold start reads the head from two different RPC operators and refuses if they disagree. An epoch that no key on the machine can open is a gap, unless a later snapshot in the same walk does open: then the pull records the skipped epoch and resumes at that snapshot, whose rows are the whole state. kint-server starts a pull as it starts, whenever the machine is connected and its cached data key has not expired, and begins serving after at most KINT_BOOTSTRAP_SECONDS (default 20) while a slower pull finishes behind it.
Verify before acting. memory_verify(query) runs the same gated search Sibyl's memory_search runs, and refuses when Sibyl's verdict is anything but ok. It re-reads the exact stored text of every hit, hashes it, and proves it against the rows_root this machine last saw anchored. When the chain head has moved past what this machine last pulled, it refuses and says to pull first. It returns proceed or refuse. A row that drifted is refused, and the reason names the block that anchored the last good value and the head block this machine last saw. The refusal is written back as a Sibyl entity (category kint_refusal, status refused), so the next fresh session sees it too. A row written after the last anchored epoch is never counted as verified. kint verify "release rule" does the same from a shell.
History across an overwrite. Because the chain keeps every version, memory_history (and kint history) lists what an entity, state or reference row held at every anchored epoch. With block it returns the version live at that block. Each version carries a block-height upper bound, "existed no later than block N", never a wall-clock time. Sibyl cannot answer this question on its own.
Snapshots and key rotation. kint compact (or memory_push(snapshot=true)) anchors one epoch that holds the whole state, flagged FLAG_SNAPSHOT (0x02). A cold start stops at the newest snapshot, so restore cost is bounded by the size of the memory, not its history. kint pull --full (or memory_pull(full=true)) walks past snapshots and caches every older epoch it had not decrypted yet, for history. kint rekey rotates the data key: a new key, new wraps under the keys you supply, one snapshot epoch under the new key, and a new recovery code. Rekey is CLI only. There is no rekey tool: rotating needs the wallet signature or the vault passphrase, and kint rekey takes them in your own terminal, never in an agent chat.
When a pull stops at an epoch that no key will ever open (a leaked session key can append one), the owner runs kint compact --over-skipped or kint rekey --over-skipped on that machine. It anchors a snapshot of the machine's store on top of the chain head, and every later pull resumes there. Neither flag is a tool argument.
Writing needs no extra step from the agent. While kint-server runs on a connected machine, a watcher pushes unanchored changes once the store has been quiet for KINT_QUIET_SECONDS (default 300), or earlier when the estimated size of the change set passes KINT_SIZE_TRIGGER_BYTES (default 32 KiB); memory_push pushes on demand, and an exit hook tries once more when the server stops. Rows a session leaves unanchored are pushed by the next session at its first quiet period. KINT_NO_WATCHER=1 turns the watcher off. The anchor always happens after the Sibyl write has returned, never inside it.
Every push kint-server makes is held, with error HELD, when a row the chain vouches for changed behind Sibyl's tools while the server was running, or still holds a value memory_verify refused. Nothing is anchored, nothing is dropped, and memory_status says why. kint push from your own terminal is never held; it is the way past a hold once you have checked the store.
The layers#
| Layer | Lives on | What it holds or does |
|---|---|---|
| Sibyl store | ~/.sibyl-memory/memory.db, or $SIBYL_MEMORY_DB | Sibyl's own SQLite file, written only by Sibyl's code. kint reads it and replays into it through the SDK. |
| kint-server | a stdio MCP process your harness starts | Sibyl's server with their eight tools unmodified, plus memory_status, memory_connect, memory_pull, memory_push, memory_verify, memory_history. |
| Local state | ~/.kint, or $KINT_HOME (mode 0700) | The machine's session key, the enrolment, the cached data key, the mirror of what was last anchored, a ciphertext cache per epoch, the freshness watermark, the head lock, the recovery code file and kint.log. |
| EpochAnchor | Base mainnet, chain id 8453 | Per owner and space, a hash-linked chain of epochs. The ciphertext travels in push calldata; the contract keeps only the latest digest, seq and block per owner and space, and emits every epoch's digest in an Epoch event. |
| kint-core | the browser | @s0nderlabs/kint-core, a TypeScript port of the decrypt path. It reads epochs off Base and opens them locally. It never writes to the chain. |
Two more details hold the layers together. First, kint's own state lives outside every path Sibyl's cap accounting walks, and kint volunteers its footprint back into Sibyl's 5 MiB free-tier cap through Sibyl's public CapGate(db_size_fn=...) hook, so kint status shows Sibyl's bytes, kint's bytes and the total against the same cap. Second, a space is keccak256("kint-space-v1" || tenant_id), so the Sibyl tenant decides which chain of epochs a machine reads and writes.
Install puts two commands on your path: kint, the CLI a human runs, and kint-server, which your harness starts. kint is not on PyPI yet; Quickstart has the install line.
Note. The signature an EOA owner signs to connect a machine is the key itself, not a login. It travels only on stdin (
kint connect --signature -), never on argv and never through a tool.memory_connectdoes acceptpassphraseandrecovery_codeparameters, but anything typed into an agent chat lands in the transcript, so prefer the terminal:kint connect --passphrase-stdinorkint connect --recovery-code-stdin.
Live on Base mainnet#
| What | Where |
|---|---|
| EpochAnchor (verified) | 0xa22E03f7a4145Bf4909a83595C90a38E14d79600, deployed at block 51081696 |
Demo tenant kint-demo, epoch 1 | 0xbe9bb8596c825a8891c3eafe4381e0c6522d647641735c635ca58fcf4bbe4455, block 51081867, 14 rows, 4 KB bucket |
Demo tenant kint-demo, epoch 2 | 0x369907fb1bccc3222365b2e07b4d6d831642831d481a5f7cb99357823e2a9729, block 51081880 |
| A Base Account owner authorizing a machine from the browser | owner 0xd3390EDAC3d0EB41248C132792B0DcC5f0b0D4E3, then epoch 0x9c64e2c21b294843888680bd34d6da8242cb5313bc894864552163e167049c12, block 51082012 |
The contract has no owner, no admin, no pause and no upgrade path; a redeploy would be a new address and a new chain of epochs. A 4 KB epoch costs about 0.000002 ETH on Base (the EIP-7623 calldata floor at 40 gas per byte, 191,040 gas for the 4 KB bucket, plus the L1 data fee). Only the bucket is public, never the true length: 4096, 8192, 16384, 32768, 65536 or 98304 bytes. Restoring and reading never send a transaction.
Who this is for#
kint is for you if you already run Sibyl Memory under a coding agent and want that memory to outlive the machine it was written on, owned by your wallet rather than by any one disk, and checked before the agent acts on it. Claude Code, Codex, Hermes and OpenClaw were each verified reading memory through kint-server on Sep 9 2026, and kint setup wires any of them. EOA owners (Ledger, MetaMask, Rabby, a keystore) sign once per machine. Base Account owners use a vault passphrase instead. Each machine writes through its own session key, which the owner authorizes with an expiry (30 days by default) and which holds a few cents of ETH on Base for gas.
It is not the right tool if several machines need to write at the same moment. One wallet owns one memory, and many machines may write under it one at a time. Two machines writing at once is a fork: kint refuses and tells you, and kint pull --discard-local moves the local store aside and restores from the chain. There is no rebase. Base holds only ciphertext, but each epoch's size bucket is public, and the bucket bounds how many rows it can carry. Limits and threat model lists the rest.
How the docs are organized#
Five groups, sixteen pages.
- Get started. This page, Quickstart (install, connect, authorize, wire a harness) and Restore on a new machine.
- Concepts. How it works follows one row from a Sibyl write to an epoch and back. Keys and custody covers the signature, the passphrase, the recovery code and the session key. Epochs on Base covers the envelope, buckets, snapshots and the walk. Verify before acting covers the decision and the refusal.
- Reference. MCP tools, CLI, Configuration, EpochAnchor contract and Browser core. Every tool, command, flag and environment variable, read from the code.
- Operate. Harnesses for Claude Code, Codex, Hermes and OpenClaw, and For agents, the page an agent reads to run kint itself.
- Background. Built on Sibyl Memory, what kint uses from Sibyl and what breaks without it, and Limits and threat model.
kint 0.3.0 is MIT licensed and was built for the Sibyl Labs Hackathon, September 2026.
Read Quickstart next.
Source: README.md, CHANGELOG.md, src/kint/server.py, src/kint/paths.py, src/kint/verify.py, src/kint/pull.py, src/kint/push.py, src/kint/connect.py, src/kint/chain.py, contracts/src/EpochAnchor.sol, contracts/deployments/base-mainnet.json.