A sovereign local brain in Python. The datasets are the mind — the models are only the handlers. Runs on a tired laptop. Signs what it knows.

NEW IN v0.3.0 — GOPHER NET · TUI + SESSIONS · STREAMING · LEARNING GATES · ONE-COMMAND INSTALL

0tests green
0cassettes
0categories
0answer tiers
0tokens on general use*

* the goal: serving is retrieval. Models compile knowledge in the background; they never serve at query time. How it gets there ↓

$ quickstart

Clone, install, run. No expertise needed.

# get it git clone https://github.com/virtuehearts/Ceti cd Ceti # install: node name, identity, embedding model, llama-server, smoke test ./install.sh --yes # run — the REPL, or /tui for the full-screen interface ./run.sh ceti> /tui

$ how it answers

Every question falls down the ladder until something answers with confidence. No freelancing. Tags on answers are honest: [local] for everything served on-box, [oracle] for cloud — with the measured tokens/sec, capped at 499.

0

deterministic calc ~0 ms · 0 tokens

Math in Python, never the model. Whitelisted AST — division by zero returns undefined, not a hallucination.

1

AIML cassettes ~0 ms · 0 tokens

Pattern-matched answers from signed cassettes. Taught facts outrank learned ones; learned outrank trained.

2

memory ~ms · 0 tokens

Vector search over everything the node has curated and learned. Paraphrases welcome.

3

local handler mode

A small model via Ollama for what retrieval can't answer. Its answers are candidates for cassettes — the next ask costs zero.

4

cloud oracle PII-gated

OpenRouter / Nvidia for the hard ones. Personal data is stripped before anything leaves the building; keys are one-time-use, never stored.

$ zero tokens on general use

The goal, stated plainly: serving is retrieval. Models act as background compilers — research, distillation, promotion — not at serve time. A question you ask every day should cost zero model tokens, because the answer already lives in a cassette or in memory.

ASKfirst time — falls to a model tier
→
CLASSIFYrelevance judge scores the pair
→
GATEStruth judge + contradiction check
→
PROMOTElearned.aiml + vector memory
→
RETRIEVALnext ask: 0 tokens

Every model answer is a candidate cassette, so the next ask costs zero. Learning is gated — see below — but serving is never slowed by the gates.

Honest limit. The local tier can still fire Ollama on a miss. The architecture pushes toward zero via learning: each promoted answer is one more question that never needs a model again. Zero is the direction, not a switch that is already flipped for every possible question.

$ learning gates

A relevance classifier alone promotes on shape, never truth — a confident confabulation scores like a fact. So promotion passes two gates before anything is written.

gate 1

truth judge

Factual promotes need one verification call. Unsure votes NO. A NO — or a failed judge — blocks learning, fail-closed.

gate 2

contradiction check

Before writing, the pair is compared against memory and AIML. A genuine disagreement blocks the write and files a curiosity gap, so research can resolve it. Elaborations are not conflicts.

Serving is unchanged by the gates — the answer is still shown. Only learning is gated. And the truth judge is the same small model checking its own output: a filter, not a guarantee. Ground truth stays the job of cassettes, research, and the contradiction check.

$ it learns while you sleep

After 10 quiet minutes, the heartbeat triggers one research cycle. A failed ask becomes a curiosity task; curiosity becomes research; research becomes a cassette. The model works the night shift so retrieval can work the day shift.

GAPa question nobody could answer
→
CURIOSITYthe gap joins the task queue
→
RESEARCHWikipedia-only, judged
→
CASSETTEnew knowledge, signed
0topics researched overnight
0Q&A pairs produced
0new cassettes

Background research stays Wikipedia-only. Idle time is the training budget — time replaces parameters.

$ the terminal UI

A full-screen curses interface — /tui in the REPL. Same brain as the line REPL, which stays as fallback.

CETI // CETI-E1C8-… // when was the c64 introduced
ceti> when was the c64 introduced
[local] The Commodore 64 was introduced in January 1982.
ceti> /ide
/identity
/ingest
/new /sessions /resume /delete /help — chats that save themselves
sessions

chats that save themselves

/new /sessions /resume /delete — one file per chat, titled from its first question, auto-saved on every line. Resume by prefix or id.

streaming

answers arrive live

Model tiers stream real chunks. Retrieval answers reveal at a simulated 499 t/s. Ctrl-C cancels — the partial answer is kept, never learned. Enter redirects mid-answer.

editing

real terminal editing

Multiline input, slash-command autocomplete (Tab), history (Up/Down), Ctrl-C interrupt.

$ the gopher net

It should feel like gopher — retro but super useful. Every peer is a public key; every post, message, and bundle is signed. /gopher in the REPL.

identity

signed peers

Peer IDs are re-derived from the public key — a card can't lie about who it is. Address is a hint; the key is the proof.

discovery

nodelist + gossip

Nodes find each other through a signed nodelist — a well-known URL or shipped with a release — plus gossip: exchanging peer lists. The whole list is refused if any entry fails verification.

board

signed posts

One signed JSON file per post. Bad signatures are rejected, never displayed.

mail

node-to-node mail

Signed direct messages. No mail to unfiled strangers — the recipient must be a peer first. Inbox verifies on load.

chat

chat rooms

Interactive rooms, one signed file per message, polled every 2 seconds. Two terminals sharing a room talk live.

files

upload / download

/upload signs a bundle into the drop; /download verifies before merging. Signed cassettes travel the same way.

Honest limit. No live transport yet — cards, posts, and bundles travel as files. The network layer is the menu, the signing, and the formats; the wire comes next.

$ trim: compression for the cloud

Given a context and the current ask, trim keeps the lines the ask needs and drops the rest — verbatim, in order, nothing rewritten. The selection is made by a 712-tree gradient-boosted model (1.2 MB, ~7 ms on one core). It is not a language model: it spends no model tokens, it just scores lines. That is why it belongs on the cloud path — it shrinks what the oracle has to read. It fails closed: a blank query, a tiny input, or a cut too small to matter returns the context unchanged.

Below is a live replay: the input is a real 302-line server log (excerpt shown), the outputs are the real results of ceti.trim on that log. Pick a question.

input — server.log · 302 lines · 19,033 bytes (excerpt)

output — what trim kept


        

Trim is wired in exactly two places: the trim tool, and the agent loop — where it trims tool output before the oracle summarizer reads it, so the error at the tail survives and the summarizer reads ~100 tokens instead of ~5,000. The oracle sees fewer tokens because trim cut the noise first.

$ the agent has hands

The oracle doesn't just answer — it does. /task in the REPL hands a cloud model the tool registry and a sandbox.

isolated

sandbox

Pure-Python Linux namespaces: read-only system, no network, private /work, rlimit caps. Foreground and background jobs. Code never runs on the host.

registry

tool registry

run_python, background jobs, file ops jailed to the work root, session notes, trim compression. JSON-schema manifests; OpenAI function-calling export for the oracle.

hot-load

skills

SKILL.md bundles — markdown + code, loaded like cassettes. file-ops, sandbox-python, web-fetch, sqlite ship in the seed set.

library

ingest

Drop a file into data/library/ and it lands in vector memory — chunked, embedded, deduplicated. /ingest runs it.

writing

/story

Oracle paper writer: story bible first, ~200-word blocks, rolling summaries, library chunks as style reference (never reproduced).

identity

signed knowledge

Node identity in data/identity.dat (0600, never leaves the box). Dataset releases are signed, chained, Merkle-rooted — verified before merging.

resilient

retry & refuse

Exponential backoff on transient cloud failures; HTTP 4xx fails fast. No API key? The loop refuses loudly instead of pretending.

$ install

One command. No expertise needed.

$ ./install.sh --yes Node name [CETI]: — you pick a name, or take the default new CETI identity: CETI-XXXX-… — data/identity.dat, mode 0600 downloading embedding model (embeddinggemma-2 Q8_0, Hugging Face) fetching llama-server (pinned b11630; nightly fallback; --build-llama builds from source) embedding server runs this model — smoke test proves it before install declares success done. Next: ./run.sh

Re-running install keeps an existing custom node name unless you pass --node-name= explicitly. The node name is the agent's display name — separate from the fingerprint prefix.

Roadmap, not shipped: live transport for the Gopher net, torrent seeding of bundles, auto-propagation, moderation, tool calls inside the TUI.

$ poke it

A fake shell with real answers. Try help.

 ceti — /task demo
you>