Prototype for AI agents
Two experiments around one question: what does an AI agent need from the outside world that a chat window does not give it? Neither is a product today. Both taught me something I use elsewhere.
Infrastructure an agent can call
A service on Cloudflare Workers that gives an agent three things:
- A way to be woken. One Durable Object per schedule sets an alarm. When it fires, the agent receives a signed webhook and the run is recorded.
- Memory it can prove. Entries are sealed and stored in R2.
- A public record. Every event goes to an append-only log in D1, where the database itself rejects updates and deletes. The log has Merkle checkpoints and is signed with the agent’s own Ed25519 key.
It is exposed as an HTTP API with an OpenAPI description and as an MCP server. 19 tests run against a local D1 and local Durable Objects.
Four decisions in it:
No queue. Delivery and retries run on the alarms of the Durable Objects, and a daily reconciliation repairs any alarm that was lost. At this scale it costs nothing and it is enough.
“Witnessed” has to be a fact. The log claims that an outside party has seen its latest state. It would be easy to write that word when the notification is sent. Instead a scheduled job outside the service publishes the head of the log, and the service marks an entry as witnessed only when it reads that head back from the public file. The label describes something that happened elsewhere, not an intention.
No cryptography dependencies. Ed25519, SHA-256, HMAC and AES-GCM all come from the WebCrypto built into the runtime. Besides one dependency fewer, it means the offline verifier, which runs in Node, shares the same routines as the service it checks.
Not obfuscating the server. The request was that end users should never see the code. Encrypting or obfuscating a Worker achieves nothing, because the platform runs it in the clear, and it damages maintenance. All the logic is on the server and the client only ever receives JSON, so the architecture already met the goal.
A memory that stays on my machine
Notes are plain Markdown files. An indexer written in Python with no dependencies turns them into a SQLite index with full-text search and vectors from a local embedding model. Search is hybrid: half the score is BM25 over the text, half is cosine similarity with the query. Any assistant reaches it over MCP.
The Markdown is the truth and the index is disposable. It can be deleted and rebuilt from the files. Without the embedding model, search falls back to text only. Without the note-taking application, the files are still readable. Each layer can disappear without taking the ones below with it.
It proposes; I approve. The system can sort an inbox of notes, but it only writes proposals. Nothing moves until I apply them. Forgetting a note archives it rather than deleting it.
Vectors from different models are not comparable. The index records which model built it. If the current model is a different one, search warns and falls back to text instead of comparing vectors of different sizes, which would return numbers that look like similarity and mean nothing.
The embedding model had to be multilingual. The numbers are in this article.
- TypeScript52.9%
- HTML24.8%
- Python18.0%
- CSS2.8%
- Shell1.4%
- Other0.1%