@metaend

MetaEndhttps://paragraph.com/@metaend "MetaEnd" delves into the frontier of AI and blockchain through in-depth discussions on innovative tools, coding techniques, and their multifaceted impact, complemented by daily industry news updates.Sun, 19 Jul 2026 02:14:57 GMThttps://validator.w3.org/feed/docs/rss2.htmlhttps://github.com/jpmonette/feedenMetaEndhttps://storage.googleapis.com/papyrus\_images/bdcb4e539c22d3c05cc31723fd652792.jpghttps://paragraph.com/@metaend

All rights reserved<![CDATA[A private two-model AI stack on one 8GB GPU]]>https://paragraph.com/@metaend/a-private-two-model-ai-stack-on-one-8gb-gpu 1JH7LdgAjgFIwg80Mid9Sat, 11 Jul 2026 14:50:36 GMTSome data should never leave the building. Bank statements, payment rails, customer records, anything you would not paste into a hosted chatbot. We wanted an assistant that can reason over exactly that kind of material and call real tools against it, with a simple rule: no third party ever sees the prompt or the data. The only network traffic the models make is to localhost.

The constraint that makes this interesting is the hardware. One consumer GPU with 8GB of VRAM (an RTX 3070 Ti), a six-core CPU (Ryzen 5 5600X), and 64GB of system RAM. That is not a lot of VRAM for an agent that both plans and generates. The trick was to stop treating "the model" as one thing.

Two models, two jobs, two devices

We run two models with different roles, and we put them on different hardware so they never fight for memory.

The first is the router. It is the tool-manager: it reads the request, decides which tools to call, calls them, reads the results, and decides what to do next. For that job we use LFM2.5-8B-A1B, a mixture-of-experts model from Liquid AI. It has 8.3B total parameters but only about 1.5B are active on any given token, and it does native tool calling. The "only 1.5B active" part matters, because it means the model is fast even on a CPU. So the router lives mostly in system RAM, and we offload about half of its layers to the GPU as a slice. The slice is tuned to a hard limit: enough to quicken the router, but small enough that the worker still stays fully resident on the same card, so the two never swap.

The second is the worker. It does the actual generation and analysis, including anything a hosted model tends to refuse for no good reason (security research, adversarial examples, blunt financial reasoning). For that we use an uncensored build of Gemma-4-E2B, quantized to a high-quality 8-bit and paired with its vision projector. It sits fully on the GPU, sharing the card with the router's slice but with room for both.

The router calls the worker as just another tool. Ask a question that needs open-ended generation and the router emits a tool call to the worker, gets the text back, and folds it into its answer. Everything runs on the same box.

you -> router (LFM2.5, CPU + GPU slice, tool-manager)
          |
          +-- read_file / list_dir / search_files   (sandboxed)
          +-- ask_uncensored -> worker (Gemma, GPU)
          +-- http_get (https only, allowlist, approval gate)

Security is the point, not a feature

Because the target is finance, the tool layer is built defensively from the start. Tools are Python functions with pydantic-typed arguments, so every call is validated before it runs. File tools are sandboxed: a path is resolved to its real location and rejected if it escapes the workspace root, so a symlink or a ../../ cannot walk out. Anything that touches the network or changes state is gated behind an explicit approval step, and it fails closed. Run the agent non-interactively and a network tool is denied automatically, because there is no human present to approve it. Secrets come from environment variables, are never written to disk by the tool, and are redacted from the audit log. Every tool call is logged, redacted, to a local file.

Adding a tool is deliberately boring: write a function, annotate its arguments with a pydantic model, and it appears in the registry with a generated JSON schema the router can call. A bank API connector is the same shape as the built-in file reader, plus an allowlist and an approval gate. The connector for a specific bank is the one piece we do not ship, because that needs a specific API. The secure pattern around it is done.

The whole thing also exposes itself over the Model Context Protocol, so our coding agent can call the local stack as a tool without any of the data leaving the machine.

Sandboxed tools, and a compliance trail

The tool layer above is the fence around our own code. The next step was to run tools we did not write, safely, and to be able to prove afterward exactly what ran. So the router now drives WasmBox, a small launcher for sandboxed WebAssembly tools. Think of it as a Flatpak for .wasm: install a tool, verify it, and run it with only the capabilities it declares and nothing more.

We wired the installed wasmbox CLI in as first-class router tools, so the LFM2.5 router can call them on-device. Each one is a capability-zero wasm binary, hash-verified before it runs and sandboxed to stdin and stdout only: no filesystem, no network, no clock. The set we exposed is small and practical: jfmt, secretscan, b64, compact, epoch, hashit, yamlfmt, diffsummary, errparse, and worldid-verify. A tool that can only read stdin and write stdout cannot exfiltrate anything, by construction, whatever it claims to do.

The reason this matters beyond safety is the record it leaves. WasmBox keeps a policy of which tools are approved and a signed, append-only audit log of every run. That is the shape the EU AI Act asks for: traceability of what the system did, and a human in the loop for changes to it. Installing a new tool is approval-gated, the same fail-closed step the network tools already pass through, and the log is Ed25519-signed, so an entry cannot be quietly altered after the fact.

The stack now has two security layers that compose rather than duplicate. localai brings the approval gate, the redacted audit log, and the path sandbox around our own tools. WasmBox brings per-tool capability grants, hash verification of each binary before it runs, and signed compliance reports for the tools it hosts. A sensitive run passes through both.

To make sure this was real and not just a diagram, we built the use case it is meant for. We loaded a reference dataset of European UCITS ETFs (IWDA, VWCE, SXR8, AGGH) and a sample portfolio, and exposed them as local tools: etf_quote and portfolio_value to read and value the book, and portfolio_buy and portfolio_sell to trade it. Reading is ungated; every trade is a state change, so it passes through the same approval gate as a network call, and it is refused outright when the cash is not there.

Then we let the router run it, not us. From a single instruction, value the portfolio, buy twenty shares of one ETF, sell a hundred of another, and value it again, the LFM2.5 router made the four tool calls in the right order and approved each trade once at the gate, the whole sequence on-device in about a minute. It read a book worth €30,643 against €5,000 of cash; the buy took cash down to €2,896, the sale brought it to €3,381, and the total held at €30,643, because trading at the reference mark only shifts value between cash and holdings. Every call landed in the signed log, and nothing left the machine. The one piece we still do not ship is a live price feed; the reference dataset stands in for it, and the fence around it is built.

One honest limit. The WasmBox tools run through the CLI as a subprocess rather than in-process, so there is a small wrapper cost on each call. The models are still the local two-model stack described above, unchanged. And as before, nothing here reaches a third party: the sandbox, the hash check, and the signed log all run on the same machine.

Numbers

Measured on the hardware above, both models resident at the same time.

Worker (Gemma-4-E2B uncensored, 8-bit, GPU):

Router (LFM2.5-8B-A1B, 4-bit, CPU with a GPU slice):

The GPU slice is sized so both models stay loaded together: about 6.2GB of the 8GB card in use, the worker fully resident and the router half-offloaded, with roughly 1.6GB to spare and no model swapping between turns. A full two-hop agent task (router decides, worker generates, router reads the result and answers) still completes in roughly thirty seconds, because that time is dominated by the router's reasoning pass and the cold model loads rather than by where the layers sit; the slice helps most when the router has a long chain of tokens to work through.

On refusals: the worker's author reports zero refusals across their own test battery. We did not reproduce that number, but in our own use for legitimate security work it answered questions a hosted model routinely declines, which is the entire reason it exists in this stack.

Where it works and where it does not

This is a small setup, and the honest limits are the useful part.

The worker is a 2B model. It is quick and it is uncensored, but it is not a frontier reasoner. Long multi-step logic, subtle nuance, and very long coherent output are not its strengths. Treat it as a fast local generator, not as a replacement for a large hosted model when the task needs one.

The router's latency is real. A two-hop task lands in the low thirties of seconds, fine for a background finance job and slow for a snappy interactive chat. lThe obvious lever, and the one we took, is to give the router a slice of the GPU rather than pin it fully to the CPU: about half its layers offload, which quickens its reasoning while leaving the worker fully resident, with no swapping. That slice is deliberately capped. Push much past half on this 8GB card and the load runs out of memory, or the worker gets evicted and the two begin swapping, which is worse than a slower router. So the real way to make this fast is a bigger card, not a bigger slice.

Vision works today because the current local runtime accepts a separate projector alongside the model. Older runtimes ignore it. If yours does, the fallback is to run the weights directly with a projector flag, and the setup notes exactly that.

Finally, the bank connector itself is not written. What is written is the fence around it: sandbox, allowlist, approval, redaction, audit. That is the part that is easy to get wrong, so that is the part we built first.

None of this is exotic. It is ollama, a few hundred lines of typed Python, and a decision to split one model into two. The payoff is an agent that can work on the numbers you cannot send anywhere else.

Feel free to reach out to me if you have questions

]]>metaend@newsletter.paragraph.com (metaend)<![CDATA[When the model can already see the page]]>https://paragraph.com/@metaend/when-the-model-can-already-see-the-page 3b5PmMSh4oc1qzKmcRczSun, 05 Jul 2026 13:34:39 GMTMost document work that reaches an AI agent has already been flattened. A PDF of an architectural drawing, a scanned datasheet, a standards document dense with clause numbering and nested tables: by the time it lands in a model's context it is usually a text dump. Someone ran extraction or OCR, concatenated the strings, and handed over a wall of characters. For prose, that is fine. For layout-rich documents it quietly deletes the part that carried the meaning.

Geometry is information. On a floor plan, a label three millimetres from a wall segment belongs to that wall; the same label centred in a room means something else. Reading order in a two-column standard is not top to bottom, and a naive dump interleaves the columns and turns a requirement into nonsense. A table is a grid of relationships, not a run of cells. Strip those out and the model is left reasoning over a transcription that lost exactly the structure a human uses to read the page. This is not a corner case for us. We build verifiable digital passports for buildings, where the source material is drawings, spec sheets, and regulatory PDFs, and where the geometry and the tables are the content, not the decoration.

So the question we set out to answer was narrow and practical: what is the right way to put a layout-rich document in front of an AI agent?

What DocLang is, and the part most write-ups get wrong

One candidate is DocLang. It is an open, AI-native markup format for unstructured content, designed to preserve structure, semantics, layout, and geometry rather than throw them away. It is real and current: doclang==0.7.2 on PyPI, Apache-2.0, governed under the LF AI & Data Foundation, the vendor-neutral open-source home that also hosts a number of production ML projects. The geometry is not hand-waving. A DocLang document places content with location blocks: coordinates on a resolution grid, 512 by 512 by default, with layers that separate body content from background and furniture. A drawing's spatial relationships survive into the markup.

Here is the nuance most write-ups miss, and it matters. The DocLang toolkit does not convert anything. Its public surface is two functions, pack() and validate(), plus a matching CLI. pack() takes an already-authored DocLang markup file and zips it into a .dclx archive, an OPC container from the same family as .docx. validate() runs the schema and rules. There is no unpack(), and there is no PDF, DOCX, or OCR importer anywhere in the package. Hand the toolkit a real PDF and it has nothing to do with it.

The converter is a separate project: IBM's Docling. Docling does the hard part, parsing PDFs, Office files, and scanned images with layout analysis, reading-order detection, table structure (via a dedicated table model), and OCR, and it can export DocLang directly. So the honest pipeline is two tools, not one:

PDF / DOCX / scan  ->  Docling (OCR, layout, tables)  ->  .dclg markup
                   ->  doclang pack / validate         ->  .dclx  ->  model

In code, the front end is small:

from docling.document_converter import DocumentConverter

doc = DocumentConverter().convert("drawing.pdf").document
doc.save_as_doclang_archive("drawing.dclx")   # writes the OPC archive directly

and the toolkit is what stands behind it as the validator and packager:

doclang validate document.xml     # runs XSD, then Schematron
doclang pack document.xml -o out.dclx

Docling is the engine; DocLang is the representation it emits and the toolkit checks. Naming that split correctly is half the battle, because plenty of material treats "DocLang" as if it ingests your documents. It does not. If you plan around the toolkit alone, you will get to the first PDF and stop.

The twist that makes this interesting

Here it stops being a tidy tooling story. The model we run is natively multimodal. It does not need a text transcription of a page. You can hand it the page image, or the PDF itself, and it reads the layout, the tables, and the drawing with its vision path. So structured markup is not competing against raw text alone. Its real competitor is the picture.

That reframes the whole evaluation. The interesting question is not "does DocLang beat an OCR dump," which it almost certainly does. It is "does structured text markup beat simply showing the model the page." And the answer is not obvious, because the two approaches trade off on different axes.

Native vision is expensive in tokens. Feeding a document as pages means each page is processed as an image as well as text, and the vendor documentation is blunt that this costs far more than clean extracted text: by one published comparison of text-extraction against full visual processing, on the order of seven times more for the same pages. Guidance from the same source is to convert to text or markdown first precisely to avoid image-based tokenization. Across a corpus of multi-page passport documents, in an agent loop that re-reads its context on every step, that multiplier is a real bill and a real pressure on the context window.

So DocLang's opening is not "more accurate." It is quality-per-token. If structured markup preserves enough geometry to answer the same questions at a fraction of the token cost of feeding the raw image, it wins on the frontier that matters for a production agent. If native vision already recovers the geometry well enough, then for exactly the drawings and schematics we care most about, the extra moving parts of a Docling-to-DocLang pipeline may not earn their place. That is the intellectual core of this, and it is open.

What we verified, and what is still open

We did not stop at a plan. We ran install spikes, and then a preliminary version of the evaluation itself, before writing the conclusion down.

What installs and runs. On CPython 3.14.6 with uv, doclang==0.7.2 installs clean, and so does its optional Saxon validation backend, saxonche==13.0.0, a prebuilt wheel with no C toolchain. IBM Docling, docling==2.109.0, installs on the same interpreter, though it pulls a heavyweight machine-learning stack, a multi-gigabyte download once the CUDA and Torch wheels land, and its DocLang export path runs live: a conversion produced valid DocLang markup and a round-tripped .dclx archive. One gap shapes any product plan: DocLang ships no renderer, so turning a .dclx back into something a person can read is work you own.

What the interim numbers say. We ran the token, validation, archive, and reasoning scenarios against a real but unrepresentative corpus: the DocLang project's own 44 valid and 59 invalid example files, not building passports, so treat the numbers as directional. Validation is effectively free and correct. Schema-only checks ran in about four milliseconds per file, full schema-plus-rules in about twenty, and every one of the 59 malformed files was rejected. Packing round-tripped losslessly on all 44. The token result was the blunt one. Measured with a consistent tokenizer, DocLang markup ran to a median of roughly twelve times the token count of the same document's plain text, and not one file came within the ten-percent overhead people sometimes hope for. DocLang is not a way to save tokens. It is a way to keep structure, and structure costs.

The reasoning result was the one worth sitting with. We asked the model the same questions from flattened text and from DocLang markup, and had a different model grade the answers blind, because a model asked to grade work in its own style tends to flatter it. On this small set the two arms were almost even. The model answered questions about tables, empty cells, and text split across pages correctly from the flattened dump too. DocLang's only clear win was geometry: asked for the coordinates of an element, the plain-text arm scored zero, because the coordinates simply are not in the text, while the markup arm had them. That is a genuine result and a narrowing one. On small text-and-table documents a strong model does not seem to need the structure spelled out. Where DocLang uniquely helps is exactly where the information is spatial and a text dump cannot carry it.

What is still open, plainly. We have not settled the question this whole post is about, because the decisive arm is still missing: DocLang markup against the native page image, on real drawings, scored for quality per token. The model can already see the page, and our interim run compared markup only against text, not against vision. The corpus was the project's own fixtures, not the dense, multi-page passports we actually care about. Fix both and the answer turns from a guess into a measurement. Until then, adopting DocLang is a preliminary yes for geometry-heavy documents and an open question everywhere else.

Where this leaves us

The architecture is promising, and the reason is fit, not novelty. We build infrastructure whose whole point is that a claim can be checked later. A document representation that keeps geometry and structure, validates against a published schema, and packs into an inspectable archive suits that posture far better than an opaque text dump or a screenshot. If it clears the vision test, DocLang is a clean layer for layout-faithful document AI, the kind you want under a building passport that a buyer, an insurer, or a regulator might one day audit.

But the adoption decision is gated, and we are keeping it gated. Promising is not proven. The three-arm test decides it, and native vision is a serious competitor that we intend to measure rather than assume away. If structure loses on quality-per-token for the documents we care about, we will say so and reach for the picture.

If you are feeding layout-rich documents to an agent and quietly assuming the geometry did not matter, it probably did. We are working through this in the open, mistakes included. If you have already run the structure-versus-vision comparison on real documents, or you think we are about to learn something the hard way, we would like to hear it.

The projects

DocLang, the specification and reference toolkit: github.com/doclang-project/doclang. IBM Docling, the converter that parses your documents and emits DocLang: github.com/docling-project/docling.

Contact the author

I write about AI-native documents, verifiable infrastructure, and agent systems as metaend. If you want to reach me, build on any of this, or tell me where it breaks, the door is here:

metaend on Quilibrium

More writing lives at paragraph.com/@metaend.

If you care whether your own content is legible to machines and agents, that is the question metaend Grade is built to answer: metaend-grade.fly.dev.

Written by metaend.

]]>metaend@newsletter.paragraph.com (metaend)<![CDATA[The evaluator is the floor]]>https://paragraph.com/@metaend/the-evaluator-is-the-floor wlSTLPsgz0l0wPqdvzxtMon, 29 Jun 2026 17:39:38 GMTWe run our company as an org chart of AI agents. This week we read a paper on loop engineering. Most of it confirmed choices we had already made. One claim did not, and it was the load-bearing one: in an agentic system, generation is nearly free, and judgment is the scarce resource. An agent asked to grade its own work rubber-stamps it. The fix is not a better self-critique prompt. It is a separate, skeptical evaluator that does not read the output and nod, but acts: it runs the code, re-derives the numbers, re-opens the cited sources, and judges against a written bar. Verification is the floor of the loop, not a decoration on top.

That landed on a real gap. Our pipeline already reserved an evaluator slot: every department head could spawn a reviewer, and the delivery pipeline had a review stage. But the reviewer itself was never defined, so reviews quietly fell back to a generic one. We filled the slot. The new reviewer starts from REJECT, never praises, and verifies by acting, branching by work type (build and tests for code, re-derived numbers and re-read sources for research and compliance) before ending with an explicit PASS or REJECT. It runs on a different model tier than the agents that produce the work, so it does not inherit their blind spots. And every delegation now carries a checkable acceptance criterion, a done-when line, that the reviewer judges against.

Then we dogfooded it. The reviewer's first real job was our own data-protection compliance framework. It did not skim: it ran the encryption status check, verified our signed approval events, hit the engine's health endpoint, and read every file. It came back REJECT, catching three real consistency defects a self-grader would have waved through: a stale status in one document, an inconsistent name across notices, and a dead cross-reference. We fixed all three. (The framework is still pre-adoption, pending qualified legal review, and we hold no certification; the point here is the reviewer, not the framework.)

The lesson we keep relearning: correctness is not a step you bolt on at the end, it is the floor you build on. Generation is cheap now. Judgment is the part worth engineering. On its first run, the evaluator earned its keep.

Contact the author

I write about agentic systems, evaluation, and verifiable engineering as metaend. If you want to reach me, build on any of this, or tell me where it breaks, the door is here:

metaend on Quilibrium

More writing lives at paragraph.com/@metaend.

]]>metaend@newsletter.paragraph.com (metaend)<![CDATA[We pointed our AI agents at their own inference bill]]>https://paragraph.com/@metaend/we-pointed-our-agents-at-their-own-inference-bill UENXA3xch3BJZmon3aHXFri, 26 Jun 2026 18:30:47 GMTWe run what behaves like a small company of AI agents: one orchestrator that plans and delegates, and six department heads that each own a single slice of a git repo. Seven agents in total, one manager, every consequential decision signed and written to an append-only audit log. The whole thing runs on omp (omp.sh / Oh My Pi), a coding-agent harness. (Aside, because the names collide and language models keep conflating them: omp here is the coding agent, not OpenMP, the shared-memory parallel-programming API. Different thing entirely.)

We showed how we run that company structure in Running a company as an org chart of AI agents, and how we added oversight in Keeping a human in the loop over a company of AI agents. Before we turned the company outward to chase revenue, we gave it one last internal tune-up. We pointed the agents at their single largest variable cost, remote inference, and gave them one rule before they were allowed to change anything: measure first.

That rule earned its keep immediately.

Measure before you migrate

The plan was the obvious one. Remote frontier models are expensive and rate-limited. A lot of agent work is token-heavy but not intelligence-heavy: summarizing, classifying, extracting, digesting verbose tool output. Push that cheap-heavy tier onto a small local model on the desktop GPU, keep reasoning and code generation remote, and the bill should drop.

So the first milestone was pure telemetry, and it was a hard gate: nothing routed, nothing trained, until a baseline report existed. The harness already writes per-call usage to local session logs, so this was a read, not an instrumentation project. Over a 23-day window the company had made 5,846 remote model calls, burned 1.34 billion tokens, and spent $1,146.81, about $49.90 a day.

Then the number that rewrote the plan. Of those 1.34 billion tokens, 96.2% were cacheRead: context the system had already sent, being re-sent and re-billed on every step. Re-reading cached context alone cost $647.78. Actual new generated output, the part that looks like thinking, was 20.2% of the bill. And the tier the whole plan was built to capture, discrete low-complexity agent tasks, turned out to be 53 calls and $1.04, which is 0.09% of spend, already running on the cheap model.

The cheap-heavy work we set out to route away did not exist as a routable set of agents. The orchestrator was re-reading 228 cached tokens for every token it generated, peaking at 413x on one heavy day. The cost was not task mix. It was repeated payload inside reasoning sessions. The measurement caught the plan's core assumption being wrong before a single byte of traffic moved. That is the entire reason the gate exists.

Standing up a local tier anyway

The reframe pointed at compression, not routing: shrink the verbose tool outputs before they enter the context that gets re-cached every step. That still needs a competent local model, so we stood one up.

The pick was Qwen3-4B-Instruct-2507 at Q4_K_M, served by Ollama. On a consumer RTX 3070 Ti with 8 GB, it decodes at about 141 tokens a second, sits fully on the GPU at 6.3 GB with the context window we chose, and takes 2.5 GB on disk. Comfortably past the 50-to-100 tokens-a-second we expected from a small quantized model, because it never spills to CPU.

We deliberately chose the non-thinking instruct build, not a reasoning variant. For digestion work, a thinking model spends its generation budget on a private monologue and can return empty content. The dedicated instruct split emits no reasoning, so every output token is useful, and a whole class of failure cannot happen. More on that failure below, because we walked into a close cousin of it anyway.

The compression hook, and the fail-safe that matters more

The real work was a hook that fires on a successful tool result. If the output is verbose enough and on the allowlist, the local model digests it before it ever enters the cached context, with a prompt tuned to lead with errors and final status and to preserve every exit code, file path, line:col, URL, identifier, and number verbatim, dropping only repetition and progress noise.

On real bash output the mechanism works well. Measured against live token counts, it cut three log samples by 95%, 92%, and 84%, roughly 90% on average, and preserved every critical token: the TypeScript error codes, the failing test assertion, the SQLite error, the exact file.ts:line:col locations, the exit codes. Faithful, not just smaller.

The part we are most confident about is not the compression. It is the fail-safe. The hook ships off by default. The committed default is all-remote. It is allowlisted to bash only. It hard-excludes read, edit, and write, because those carry code and exact data the agent needs byte for byte. It runs a cached health check, and if the local model is unreachable it passes the original output straight through. We proved this with the desktop effectively off: the agent received the full, undigested output, the run exited 0, and there were zero manual steps. A cost optimization that can break a tool call is not worth having. This one cannot. When local is down, nothing about the company changes except the bill.

The honest twist

Then we measured what the hook would save in production, and it is small.

Across the entire 23-day history, only 12 bash outputs landed in the hook's size window, totaling about 145 KB. Bash output is overwhelmingly tiny: 88% of it is under 1 KB, things like git status and one-line pipelines. Bash is just 3.8% of the tool-output bytes worth digesting. The real volume is read output, 71% of it, and read is exactly what we exclude on purpose, because code and data have to survive verbatim. The big budget line, the 96.2% cacheRead, is mostly the system prompt, tool definitions, and reasoning history being re-read, not tool output at all.

Apply the measured 90% reduction to that thin slice and the projected saving at the committed default is well under $2 over a comparable 23-day window, under 0.2% of the bill, with no measurable reduction in rate-limit hits. The harness was also already doing most of this work: it compacts context de-deterministically, protects the newest tokens, and trims many command outputs before our hook ever sees them. We are picking up a residual on a residual.

So the win is not a smaller bill. The win is two things. One, a fail-safe pressure valve for the rare bash-heavy batch day, free and near-zero risk. Two, and this is the real prize, the measurement itself: we now know exactly where the money goes, which means we know that the only way to grow the saving is a faithfulness project on read and search digestion, where the volume is. That is a real project with a real risk (lossy-digesting code is dangerous), gated on the same per-task validation we used for bash. It is the honest next step, not a number we can claim today.

Where it works: bash-heavy batch sessions, as a safety valve, with full recovery of the original output always available. Where it does not: anything latency-sensitive (the local decode adds a few seconds), and the budget at large, because the digestible bash volume is small and the harness already captures most of it. The upgrade path is faithful read and search digestion, validated before it is trusted.

Five things omp taught us the hard way

The fun of a project like this is in the traps. These are real, and grounded in the work.

The empty-output trap. Ollama's OpenAI-compatible endpoint silently ignores a per-request context size. The harness read the model's trained context of 262,144 tokens, Ollama's actual default was 4,096, so it quietly truncated and returned nothing at all. The model worked in isolation and produced empty output through the harness. The fix was to bake the real context window into the model definition itself rather than pass it per request. This is the cousin of the thinking-model trap, and the reason we chose a non-thinking model: two different roads to the same blank reply.

The memory model that was never local. The config pointed memory operations at a small ONNX model and had for a while, so on paper that work already ran locally and for free. It did not. That model is disabled in the current harness version (a broken operator in the runtime), so memory had been silently falling back to remote the whole time. "We already do this locally" was false, and only checking it surfaced that. We swapped in a model that loads.

The harness already ate the easy savings. Before our hook sees a tool result, the harness compacts context, protects the newest 40,000 tokens, and minimizes shell output. A 19.6 KB git log --stat arrived under 8 KB, so our hook correctly skipped it. Over the window the harness had already shaken 560 bash outputs down to pointers. Respect what your tools already do, or you will proudly reinvent it and measure a phantom win.

A model that vanished mid-build. The sharpest taste of the problem was first-hand. One of the milestone agents was wired to a remote model id that returned a 404, not found. The subagent did not degrade or retry, it died on spawn, and we re-ran that milestone on a working model. A model that is simply gone is the most extreme version of the availability pain the whole project was aimed at, and it happened to us while we were aiming at it.

The bill tasted its own medicine. The project ran into the exact friction it set out to study. The 23-day window logged three rate-limit exhaustion events plus one warning, with the five-hour quota window hitting 100% on the two heaviest days. The cost and the limit pressure are the same mass, repeated payload, on the same days. And the milestones of this very effort added about $30 of new spend, all of it remote, because the hook was correctly still off. We were paying the problem while we measured it.

Self-improvement, with brakes

The thing I find most interesting is not the hook. It is that the company improved a piece of itself without anyone losing control of it.

The orchestrator did not do the work. It fanned the work out to department-head agents, milestone by milestone, with a hard gate at the front: the baseline report had to exist before anything could route or train. Every shipped milestone carries a signed decision and a thread you can replay, recorded in a single records-office daemon that is the sole writer of the company's memory. Knowledge is append-only; a revised finding supersedes the old one rather than overwriting it, so the trail of what the company believed, including the wrong first assumption, is intact.

The constraint that made me trust it was the boundary. The agents doing this work are scoped to the repo. The routing and the hook all live inside the repo, project-local. Nothing was written to the harness's own machine-local config outside that boundary, by policy, because crossing into a human's machine configuration is the kind of step an autonomous agent should not take on its own. When the work brushed against that line, it stayed inside and noted it, rather than reaching across. Self-improvement is easy to demo and hard to keep safe. The brakes (a measurement gate, signed decisions, an append-only record, a folder boundary the agents respect) are what make it something you would run.

If you care about whether agents leave a checkable trail, that posture, verifiable decisions and honest limits over confident claims, is the same one we apply to the products we ship, including metaend Grade, our agent-readiness scanner. The pattern travels.

What we would tell another builder

Four lessons, all boring, all the point. Instrument before you optimize: the baseline was the most valuable artifact here, and it killed the original plan in the first milestone, before we could spend weeks routing a tier that did not exist. Make the optimization fail-safe and off by default: this layer is reversible, self-disabling, and all-remote until you opt in, which is why we can ship it without a knot in the stomach no matter how small the saving. Respect what your tools already do: half our theoretical win was already captured by the harness, and measuring that saved us from claiming it twice. And correctness over cleverness: the clever version digests read output too and shows a bigger number, the correct version leaves code verbatim until a faithfulness project earns the right to touch it. We shipped the correct version and wrote down the path to the bigger one.

The result is not a headline cost cut. It is a proven, reusable mechanism, a fail-safe pressure valve, and a precise map of where the money goes. For a last tune-up before the real work begins, knowing the territory is worth more than a small saving we would have had to oversell.

Contact the author

I write about agent infrastructure, local inference, and privacy-first systems as metaend. If you want to reach me, build on any of this, or tell me where it breaks, the door is here:

metaend on Quilibrium

More writing lives at paragraph.com/@metaend

]]>metaend@newsletter.paragraph.com (metaend)<![CDATA[Keeping a human in the loop over a company of AI agents]]>https://paragraph.com/@metaend/human-in-the-loop-over-a-company-of-agents 23pG2azife4qtg0hJU7mThu, 25 Jun 2026 09:23:29 GMTIn our last post we showed how we run our whole company as an org chart of AI agents: a manager that plans and delegates, folder-scoped department heads that each own one slice of the repo, a single daemon as the records office, and Nostr-signed decisions for the paper trail. The whole thing is reproducible and auditable, and most of the time it runs itself.

"Most of the time it runs itself" is the part that should make you pause. An autonomous company that plans, decides, and signs its own work still answers to two people: the two of us who own it. The two things we added since that post are both about the same problem, putting a human back in the loop. One is a private assistant we talk to. The other is a cockpit we watch. Neither makes the agents more autonomous. Both make them more accountable to us.

Shadow: an assistant you reach over a private DM

We layered a personal assistant on top of the company. We call it Shadow. It is not another department in the org chart. It sits above the chart and works for the two of us directly.

Shadow runs on a separate autonomous-agent runtime (the hermes agent), inside a rootless Podman sandbox. The sandbox is the point: it lets us hand an assistant real tools, a shell, files, the company's task queue, without handing it the run of the host machine. Secrets are masked out of the sandbox by mounting over them, so even with the vault unlocked in the working tree the assistant cannot read the keys.

We reach Shadow two ways. From a terminal when we are at a desk. And, the interesting one, by private direct message over Nostr, so we can check on or steer the company from a phone with no dashboard and no VPN.

The DM channel is NIP-17 private direct messaging, carried over NIP-59 gift wrap. A message is built up in layers:

your text
  -> kind:14  rumor      (the plaintext message, unsigned)
  -> kind:13  seal       (NIP-44 encrypted, signed by your real key)
  -> kind:1059 gift wrap (NIP-44 encrypted to Shadow, signed by a throwaway key)

The outer wrap is signed by a fresh one-time key and encrypted to the recipient, so a relay that passes it along learns neither who sent it nor what it says. That is the metadata-private property NIP-17 is designed for, and we route every encrypt, decrypt, and unwrap through the same code path the cockpit uses, so the bridge exercises the real production crypto rather than a toy of its own.

Shadow only answers people it knows. The bridge carries an allowlist of our two personal Nostr public keys. A gift wrap is unwrapped far enough to read the true sender, checked against that list, and if the sender is not one of us it is dropped in silence: the assistant is never even invoked for a stranger. Shadow's own signing key is decrypted at boot from the committed, encrypted keyring (the same NIP-49 ciphertext model as the rest of the company) and never written to a log. As with everything else here, one passphrase unlocks it all and nothing sensitive sits on disk in the clear.

What Shadow is for comes down to three jobs.

First, owner-personal work, kept isolated from the agents. Drafts, confidential notes, anything that should not land in the shared repo lives in a private workspace mounted outside that repo and never committed. It is reachable by the two of us and by Shadow, and unreachable by the autonomous department agents, whose world is the shared repo. Private from the agents, not from us.

Second, routing company work to the orchestrator. When we ask Shadow for something that belongs to the company, it does not reach into a department itself. It opens a task on the orchestrator's company-wide queue in the engine, the same queue the cockpit writes to, and the company's normal machinery takes it from there.

Third, oversight. Shadow can read the live state of the company and tell us, in plain language, what the agents are doing and what is waiting on us.

The framing underneath all of this matters more than any single feature: Shadow is accountable, never covert. Private and access-isolated are not the same as hidden. It does not conceal its own actions from the people it works for. We could not build a sneaky one even if we tried, because the agent runtime scans its own persona file and refuses instructions written as covert or insider-threat behavior, anything that says hide from the humans or evade the audit. So we wrote the opposite on purpose: discreet, isolated from the other agents, fully accountable to us. The distinction is the whole design.

The cockpit: watching the agents work

A signed audit log is excellent for "why did we ship this." It is poor for "what is happening right now." So the second addition is a visual cockpit for the company, an Astro app with a React island and a PixiJS canvas. Login is gated to a single Nostr key, ours. It reads the company's loopback engine and nothing else, and it publishes no company state to any public relay. On the cockpit, Nostr is login only.

The main view is a live top-down pixel-art office. Each department is a zone with its agent at a desk, and the number of little figures at that desk is the agent's count of concurrent sessions. When nothing is running, the office is quiet. When the agents pick up real work, the desks fill. It does not perform busyness; an idle company looks idle.

Beside the office sit three panels. A kanban rolls every department's tasks into three columns, to do, in progress, and done, with a composer to create and assign a task to any department or to the orchestrator's company-wide queue. A department detail view shows one team's head, status, queue counts, current task, and its recent thread messages, the same delegate, question, answer, report, and decision trail from the last post. An inbox groups those recent messages by agent so you can skim what the company has been saying to itself.

All of it reconciles from a single write of the engine roughly every five seconds, and tasks flow the other way as a single write. The floor, the kanban, and the inbox track the real state of the company within a few seconds of it changing. When the engine is down, the cockpit says so and falls back to placeholder counts rather than faking a running company.

From a card on a board to a real coding session

A kanban that only shuffles cards is theater. The piece that makes the board real is the task executor.

It is a small poll loop running next to the engine. Each tick it reads the same cockpit state, finds departments that have open work, and atomically claims one task per department. A claim stamps the task with an owner and a time-boxed lease, and the engine's single-writer queue guarantees that two claimers can never grab the same task. For a claimed task it spawns a real omp coding-agent session, running that department's own agent prompt, pointed at the repo:

omp -p --cwd <repo> --approval-mode write \
    --append-system-prompt .omp/agents/<role>.md \
    "<task title>

<task body>"

When the session exits, the executor writes its output back onto the task and closes it. The lifecycle is the engine's task lifecycle, end to end:

cockpit composer / Shadow  ->  open      (a fresh, unclaimed task)
executor poll              ->  claimed   (owner + a time-boxed lease)
                               omp runs the department's agent on the repo
                           ->  done       (result recorded) or failed

If omp exits nonzero the task is marked failed, its lease lapses, and a later tick can reclaim it. There are guard rails around the whole thing: a cap on how many sessions run at once across all departments, at most one in-flight claim per department, a cap on how much session output is stored, and an approval mode on every spawned session. The executor is off by default and opt-in per work session, and it runs host-side, because a spawned omp session edits the repo for real. The net effect is that a task you type into the cockpit, or send to Shadow over a DM, becomes a claimed task, becomes a running agent, becomes a completed result with the output attached.

Where this works, and where it does not

Metadata privacy is real here, and it is not absolute. The gift wrap hides the sender and the content. It cannot hide the recipient: Shadow has to be addressable for a relay to deliver anything to it, so its public key is visible on the outer event. NIP-17 also randomizes timestamps to blunt timing analysis. If your threat model needs to hide that the assistant exists at all, a public relay is the wrong transport, and the upgrade is a private relay we run ourselves.

The signing trust model is the same one we documented before, stated the same plain way. On a single-user, loopback-only box the daemon holds each role's key and signs on behalf of whatever author a local client names. That is daemon-level provenance, not per-process authentication. It is a deliberate trade for the local model, with a clean upgrade the moment the daemon leaves loopback: bearer-token auth on the endpoint plus per-caller-to-role binding, so a signature proves the authenticated caller.

The cockpit observes and steers; it does not yet gate. The one action we will not let an agent take on its own is crossing the repo boundary, reaching outside the company's own folder. The design for that approval is written down: such a step requires a human approval authenticated by our Nostr key in the cockpit, plus a signed compliance decision, before it can proceed. That approval surface is specced, not shipped. Until it exists the boundary stays closed and the cockpit stays read-and-steer only for those actions. We would rather say that out loud than imply a guard that is not built yet.

Why this shape

The pattern in both additions is the same. Autonomy was the easy part; we already had agents that plan, build, and sign their work. Oversight is the part you have to build on purpose, because nothing about an autonomous system produces it for free. A private assistant we can reach from a phone, and a cockpit that shows the true state of the work, are not ornaments bolted onto autonomy. They are how the two of us stay responsible for what the company does. The agents do the work. We stay in the loop, and we stay accountable for it.

Contact the author

I write about agent infrastructure, Nostr, and privacy-first systems as metaend. If you want to reach me, build on any of this, or tell me where it breaks, the door is here:

metaend on Quilibrium

More writing lives at paragraph.com/@metaend.

Written by metaend.

]]>metaend@newsletter.paragraph.com (metaend)<![CDATA[The EU AI Act is an engineering requirement]]>https://paragraph.com/@metaend/the-eu-ai-act-is-an-engineering-requirement fDy562rYxFpra49GR0EiTue, 23 Jun 2026 08:41:59 GMTLast month I wrote about the Instagram takeovers, where attackers talked Meta's AI support bot into handing over accounts because the bot had the authority to change account recovery and no rule about who it would do it for. The argument was simple. An agent with a powerful capability and no guardrail is not a feature, it is a social-engineering target with API access. Capability is easy. Governance is the part everyone skips.

A few weeks later, the European Union published the same argument as law.

On May 19 the Commission released draft guidelines on which AI systems count as high-risk under the AI Act, with a consultation running to June 23. Read past the bureaucratic surface and the high-risk requirements are a description of exactly the governance the Instagram bot did not have. Article 12 wants record-keeping and traceability, the ability to reconstruct what the system did. Article 14 wants human oversight, a person in the loop for consequential actions. Article 15 wants accuracy, robustness, and cybersecurity. That is not a compliance abstraction. It is the post-incident review of every ungoverned agent, written as a checklist before the incident instead of after.

So the regulation caught up to the engineering. Good. Here is where it goes wrong.

The compliance theater trap

The moment a regulation lands, an industry forms around making it go away cheaply. For the AI Act that industry is already selling governance binders: policy templates, classification questionnaires, a PDF that says you have a risk management system, a slide that says human oversight. Buy the bundle, file it, move on.

The problem is a category error. The AI Act's high-risk requirements are not document properties. They are runtime properties. They describe what the system does at the moment it acts, not what a folder says about it.

You cannot satisfy traceability with a Word document. Traceability is whether your system actually logged the action, immutably, in a way you can produce later. You cannot satisfy human oversight with a policy that says a human is involved. Oversight is whether the system actually stopped and waited for a person before it did the irreversible thing. You cannot satisfy robustness with an attestation. It is whether the thing holds when someone pushes on it.

A binder is not a control. It is a description of a control that may or may not exist. The Instagram bot's operator could have had a beautiful binder. The binder would not have sent that one-time code to the attacker any less.

If you build agents, this is you

There is a comfortable assumption among people building on top of foundation models that the AI Act is someone else's problem, a thing for the labs. It is not.

If you wire an agent on top of a model and ship it, you are a deployer under the Act. Whether you carry the heavy obligations depends on what the agent does. Land in one of the high-risk categories, hiring, credit scoring, critical infrastructure, and the full weight applies. Modify the model substantially through heavy fine-tuning and you can be reclassified as a provider, with more. The line between deployer and provider is genuinely murky right now, which is part of why the Commission is publishing guidelines and asking for feedback at all. And the Act reaches you wherever you sit. It applies to any system that touches people in the EU, regardless of where your company is. The penalties at the high-risk tier run to tens of millions of euros or a slice of global turnover, which is not a number a small team litigates its way around.

But forget the penalty for a second, because it is the least interesting reason to care. The controls the Act asks for, traceability, oversight, robustness, are the exact controls that stop your agent from becoming the next Instagram story. They are what you would build anyway if you were honest about what an autonomous system with real capabilities can do, and your classification is not frozen, it changes as your product does. The regulation is not asking you to bolt on paperwork. It is asking you to be able to answer, at runtime, what is this agent allowed to do, did a human approve the dangerous parts, and can you prove what happened. If you cannot answer those, you do not have a compliance problem. You have an engineering problem that compliance noticed.

The delay is runway, not reprieve

Here is the part that will be misread. The Digital Omnibus, agreed in May, pushed the high-risk deadlines back: standalone systems to December 2027, systems embedded in products to August 2028. Cue the exhale, the "we have years" shrug, the quiet deprioritization.

That is the wrong read. The deadline moved. The exposure did not. Every month you run an agent with real capabilities and no runtime governance is a month you are one clever prompt away from the failure the Act exists to prevent, deadline or no deadline. Prohibited practices and the rules for general-purpose models are already in force. Enforcement powers switch on this August. And the controls take real time to build, which is the actual reason the deadline moved, not a gift of idleness but an admission that doing this properly is not a weekend of paperwork.

Treat the runway as what it is. Time to build the controls before you are forced to, while the cost of getting it wrong is still just your users and not also a regulator.

What to actually build

The same thing I have been saying since the Instagram post, now with a legal deadline attached.

Scope every capability to least privilege, so an agent can only ever do the narrow thing its task needs. Put a policy layer in front of every consequential action, so the system decides whether an action is permitted before it runs and refuses what does not meet the rules. Require a human for the irreversible things, money, identity, anything you cannot take back. And log every action in a tamper-evident, signed record, so traceability is a property of the system rather than a promise in a document.

That list is not my invention. It is Articles 12, 14, and 15 described from the engineering side instead of the legal one. They converge because they are describing the same reality from two directions.

This is what I build with WasmBox: a sandboxed tool launcher for agents where every capability runs behind policy, inside a sandbox, and lands in a signed audit log. Not because the AI Act requires it, but because it was the right way to run an agent before the AI Act existed, and the Act has now made the right way also the legal way. The binder is downstream of the control.

The honest version

The AI Act is not perfect and the timeline is a mess. But strip the politics and it asks agent builders to do the one thing that was always correct: know what your agent can do, gate what matters, and be able to prove what happened. The companies that treat that as a documentation exercise will produce excellent binders and ungoverned systems. The ones that treat it as an engineering requirement will be compliant almost by accident, because they built the controls the paperwork is only describing.

A binder is not a control. Build the control, and the binder writes itself.

metaend

Try WasmBox

]]>metaend@newsletter.paragraph.com (metaend)<![CDATA[Running a company as an org chart of AI agents]]>https://paragraph.com/@metaend/running-a-company-as-an-org-chart-of-ai-agents 07f8Mefi8tQTf0p9lhY4Mon, 22 Jun 2026 12:26:34 GMTWe do not open a CRM, a research wiki, and a build console. We open one chat window and talk to a manager. Behind that manager is an org chart of AI agents: a CRM department, a geo-audit department, a coding department, a research department, each with its own memory, its own task queue, its own signed paper trail, and the ability to staff up temporary help for a busy afternoon. The whole thing lives in a git repo. Clone it, type one passphrase, run one command, and the company is standing again on a fresh laptop.

This post is the architecture and the how-to. Every command, path, and tool name below is real and lives in our repo.

The idea: a company is an org chart you talk to

You talk to one agent: the orchestrator. It is the manager. It plans, decomposes, delegates, reviews, and integrates, and it never edits domain code itself. That last rule is load-bearing: the manager stays a clean integrator, so responsibility for any given folder always has exactly one owner.

Under it sit four folder-scoped department heads, each owning one slice of the repo:

you
 └─ orchestrator        main session     plans / delegates / integrates; never edits domain code
      ├─ crm-engineer    L1 subagent      owns projects/crm
      ├─ geo-audit-operator L1            owns projects/geo-audit (pipeline operator)
      ├─ coder           L1 subagent      horizontal code (repo minus the product folders)
      └─ researcher      L1 subagent      owns research/, read-only on code
             └─ worker   L2 leaf          one objective, isolated worktree, cannot fan out

These are not vibes. They are declared agent charters in .omp/agents/*.md. The orchestrator's front-matter says exactly what it is allowed to touch:

# .omp/agents/orchestrator.md
name: orchestrator
model: plan
tools: read,search,find,task,todo,bash
spawns: crm-engineer,geo-audit-operator,coder,researcher

Each role maps to a model. The orchestrator runs on a strong reasoning model, the heads on code-strong ones, and we run open weights for all of it.

nano-gpt. A clean way to serve those role models is nano-gpt: pay per prompt, no account, pay with crypto or card, and access to hundreds of models including the open weights we lean on (DeepSeek, Qwen, Kimi, GLM). Prompts are not stored by default, there is an in-browser TEE private mode with receipts you can verify yourself, and it ships a native MCP. It fits a privacy-first agent stack without a subscription.

Each head can fan out ephemeral workers for genuinely parallel work: one worker per rule module, say, each in an isolated worktree with a disjoint file scope. omp enforces a two-level depth cap and blocks self-recursion, so a worker is a leaf. It has no task tool, can read the department database but never write it, and returns a branch plus a short report before it yields. The head merges the worktrees and owns the single authoritative write. Fan-out is opt-in, never automatic, with a soft cap of about five workers. Small work the head just does itself.

Routing is one writer per folder. projects/crm goes to crm-engineer, projects/geo-audit to geo-audit-operator, general code to coder, and "find out or synthesize" to researcher. No two heads ever write the same folder.

Why a daemon: the Records Office

Here is the constraint that shapes everything. We store each department in its own local Turso database (@tursodatabase/database, the embedded engine, local by design, no cloud, no sync). The embedded engine forbids multi-process access. If five agents each opened departments/crm.db, we would corrupt it.

So no agent ever opens a database file. Instead, one long-lived process, which we call the Records Office, the deptdb daemon, owns every departments/<dept>.db and is their sole writer. It exposes a streamable-HTTP MCP server on 127.0.0.1:8788, and every agent, whether on omp or anything else that speaks MCP, is just a client:

cd deptdb && KEYRING_PASSPHRASE=… bun src/server.ts   # or: just -f deptdb/justfile deptdb

The daemon is a bun and TypeScript program with no build step. It keeps one connection per department and runs all of a department's operations through a per-department serial queue. That is the entire concurrency model. No MVCC, no secondary indexes (both experimental in the engine), just a single writer absorbing fan-out concurrency and guaranteeing that snapshots and task claims see consistent state.

Wiring it into the org is four lines. The repo's mcp.json points every agent at the loopback daemon:

{
  "mcpServers": {
    "deptdb": { "type": "http", "url": "http://127.0.0.1:8788/mcp" }
  }
}

The orchestrator's first move every session is to call list_departments. If that fails, the daemon is not up, and nothing proceeds until it answers.

Per-department memory: knowledge, tasks, threads, audit

Each department database is four things at once.

Append-only knowledge with semantic search. A knowledge table stores title plus body plus an embedding BLOB. Search is brute-force cosine distance, vector_distance_cos(embedding, vector32(?)) ORDER BY dist LIMIT ?, which is plenty at department scale (target under about 10k rows). Embeddings come from a local Ollama model, nomic-embed-text (768-dimensional vectors), which we now run for real, with an offline deterministic hashed fallback (a smaller 256-dim vector) so the company still works with no model server at all. Note the split: embeddings stay local on Ollama, while the heavier reasoning roles run on the hosted open weights from the aside above.

Crucially, the embedder is pinned per department database. The first knowledge_write stamps the active embedder into a meta row, and every later read and write checks against it. If the daemon's embedder or its vector dimension no longer matches what a department was written with, the call fails closed instead of silently mixing incompatible vectors:

embedder mismatch for crm: db pinned to 'ollama:nomic-embed-text', daemon is 'deterministic:256'.

The operational rule is simple: keep Ollama up when the daemon boots, so every department pins to ollama:nomic-embed-text. If you would rather rule out a silent downgrade, set DEPTDB_EMBEDDER_FALLBACK=false, and a missing model server then becomes a hard boot failure instead of a quiet fall back to the deterministic embedder, which would otherwise mismatch every Ollama-pinned department on its next read.

Knowledge is never mutated. A new entry supersedes the old one (setting supersedes and deactivating the prior row), so the history of what the department believed is always recoverable.

A single-writer task queue. Tasks are opened, then atomically claimed with a time-boxed lease. Because the claim runs on the serial queue, two agents can never grab the same task, and a lease that expires lets the work be reclaimed.

Threaded discussion and a signed audit log: the durable record, covered next.

All of it is reachable through exactly 18 MCP tools, grouped:

The last group is how a department earns extra tools without forking the daemon. geoaudit.db additionally carries audits, probe_cost, and extractability_cache tables, and its operator calls audit_recent(domain) to avoid re-spending a paid probe inside a freshness window.

metaend Grade. Checking how legible a site is to an AI agent is useful on its own, outside any audit pipeline. That is what metaend Grade does: point it at a domain and it scores how readable and usable your site is for AI agents, in the same spirit as the extractability layer this department caches.

Delegation as a durable record

The reason for threads is that delegation should be replayable, not lost in a chat scroll. Every objective produces a paper trail with a small, fixed protocol:

  1. The orchestrator opens a task and a thread, then posts a delegate message describing the objective.

  2. The routed head claims the task. It either posts a question and yields (never guesses), or does the work and posts a report with its branch name.

  3. The orchestrator posts an answer to unblock a question, or, after /review and integrating the branch, posts a signed decision.

Those are the literal roles the thread_post tool accepts: delegate, question, answer, report, decision. When a head fans out, it opens a sub-thread for its workers with the same roles, so the tree is replayable all the way down to a single worker's contribution. The head merges and owns the one signed write, and workers stay keyless.

The effect is that "why did we ship this?" is always answerable. Knowledge is superseded, never overwritten. Decisions are appended, never edited. The company remembers.

Verifiable attestation with nak

Five of the agents, the orchestrator and the four heads, have a persistent Nostr identity, managed end to end with nak, the Nostr army knife. When a decision is posted, the daemon signs a kind:30078 event tagged with the department, task, and thread, stores the event JSON on the message row, and mirrors it into the audit_log. Anyone can check it later:

echo '<event-json>' | nak verify

Identities are generated once (just -f deptdb/justfile identities): nak key generate for the secret, nak encode npub for the public registry committed to identities.json, and nak key encrypt to store the NIP-49 ciphertext in the vault. At boot the daemon loads the encrypted keyring, decrypts it in memory with the keyring passphrase, and signs at runtime. Workers have no key and sign nothing.

An honest word on what this proves. On our single-user, loopback-only box, the daemon holds each role's key and signs on behalf of whatever author a local client claims. So a signature attests "the daemon, holding role R's key, recorded this decision." It is daemon-level provenance, not per-agent authentication. It does not cryptographically prove which process authored the call. That is a deliberate, documented trade-off for the local trust model (see deptdb/SECURITY.md), and it has a clean upgrade path: the moment the daemon leaves loopback, you add bearer-token auth on /mcp and per-caller-to-role binding so an attestation proves the authenticated caller. We say this plainly rather than overselling the crypto.

Reproducible and encrypted, straight from git

Live databases are disposable. departments/*.db is gitignored and rebuilt on demand. The committed source of truth is a textual SQL snapshot per department, snapshots/<dept>.sql: INSERT statements with embeddings emitted as x'<hex>' literals for an exact round-trip. Text instead of a binary .db means no WAL-sidecar corruption, no merge hell, and survival across a future engine change. The snapshot and rehydrate tools (and their CLIs) drive the loop, and snapshot runs on the serial queue so it always captures consistent state.

Three classes of state, three handling rules:

| State | Lives in | Encryption | Committed? | | Code and config | repo | none (no secrets) | yes, plain | | Data (knowledge/tasks/threads/audit) | snapshots/<dept>.sql | git-crypt | yes, encrypted | | Secrets (nak keyring, tokens) | secrets/ | git-crypt | yes, encrypted | | The git-crypt key itself | cowork.key.age | age passphrase (scrypt) | yes, encrypted | | Live databases | departments/*.db | none | no, gitignored, rebuilt |

secrets/ and snapshots/ are transparently git-crypt-encrypted in the remote (AES-256) and plaintext only in your working tree. The git-crypt key that unlocks them is itself wrapped with an age passphrase as cowork.key.age and committed. So the repo is fully self-contained and the only secret you carry by hand is one passphrase, kept in a password manager plus one offline backup, never on disk in the clear. A one-time just -f deptdb/justfile vault-init (the only place git-crypt is initialized) sets all of this up.

Standing the whole company up on a new laptop is four steps:

git clone <repo> && cd cowork
age -d cowork.key.age > /dev/shm/cowork.key            # enter the passphrase (tmpfs, not disk)
git-crypt unlock /dev/shm/cowork.key && shred -u /dev/shm/cowork.key
just -f deptdb/justfile rehydrate                      # rebuild live DBs from encrypted snapshots
just -f deptdb/justfile deptdb                         # daemon loads the keyring, signs, serves

Everything else, the encrypted git-crypt key, the nak keyring, and all department data, travels inside the repo. When you ever do get a hardware token, the only change is swapping age -p for an age-plugin-yubikey recipient. Nothing else in the design moves.

How it is built and trusted

The daemon is bun and TypeScript, no build step, with the tool surface split into small modules under deptdb/src/tools/ (core, portability, geoaudit). It ships with 77bun test cases across 11 test files, covering the embeddings round-trip, knowledge and semantic search, the task queue and serial-queue concurrency, threads and signed decisions, snapshot and rehydrate portability, the geo-audit extension, end-to-end HTTP MCP integration, and a dedicated security-hardening suite, all green, with bunx tsc --noEmit clean on top.

A security audit drove a concrete set of mitigations, each documented in deptdb/SECURITY.md:

The doc is equally explicit about the residual risks it accepts under the local single-user model: daemon-level (not per-caller) provenance, no per-tool auth on the loopback port, and full-scan knowledge search, each with the exact upgrade to make if the deployment assumptions change. Honest numbers, honest limits.

Extending it: adding a department

Growth is a pattern, not a rewrite. A department is a row in the registry, a folder, a database, and an agent charter. The whole registry lives in deptdb/src/config.ts:

export const DEPARTMENTS: Record<string, DeptSpec> = {
  crm:      { db: "crm.db",      kind: "default",  role: "crm-engineer",       folder: "projects/crm" },
  geoaudit: { db: "geoaudit.db", kind: "geoaudit", role: "geo-audit-operator", folder: "projects/geo-audit" },
  coding:   { db: "coding.db",   kind: "default",  role: "coder",              folder: "(repo minus product folders)" },
  research: { db: "research.db", kind: "default",  role: "researcher",         folder: "research" },
};

Add an entry, drop in a charter under .omp/agents/, snapshot, and the new department is a first-class citizen with all 18 tools and its own signed memory. The full, repeatable runbook, including the geoaudit-style path for a department that needs extra tables and tools, lives in deptdb/ADDING-A-DEPARTMENT.md.

What a real request looks like

Put it together with one sentence to the orchestrator: "Refresh the geo-audit on-page checker for 2026, then re-audit a domain."

  1. The orchestrator opens a task and thread, then delegates the research to researcher.

  2. researcher runs knowledge_search on the research department, fills the gaps with web_search, and writes findings with knowledge_write, each with its source in the ref. It returns the finding ulids and yields.

  3. The orchestrator delegates to geo-audit-operator, which reads those findings, sees the change spans the checker plus three rule modules, and fans out oneworker per module (disjoint scopes) while taking the checker itself.

  4. Workers return branches. The operator merges, runs a lite self-test, writes one consolidated knowledge entry, and posts a signed decision to geoaudit.

  5. Before re-auditing, it calls audit_recent(domain). Nothing fresh inside the window, so it runs the audit, then records the result with audit_record and caches the technical layer with extractability_put.

  6. The orchestrator runs /review, integrates the branch, and posts the final signed decision.

  7. just -f deptdb/justfile sync snapshots every department, the vault guard confirms snapshots/ and secrets/ are git-crypt-encrypted, and it pushes.

You wrote one sentence. The company produced working code, a paper trail down to each worker, a nak verify-able decision, and an encrypted commit, and tomorrow on any machine, rehydrate brings it all back exactly as you left it.

Why this matters

We wanted a company that behaves like software: versioned, reproducible, and auditable, not a pile of SaaS tabs and tribal memory. What we got is exactly that. You talk to a manager that delegates to specialists who remember what they have learned, justify what they shipped, and sign the decisions that matter. The data is encrypted the moment it leaves the daemon, and the entire operation collapses into a git repo you can clone anywhere.

A company you can clone, unlock with one passphrase, and rehydrate on a fresh machine in under a minute. That is the whole point. The org chart is code. The memory is portable. The paper trail verifies. And the only thing you cannot lose is a single passphrase in your head.


Contact the author

I write about agent systems, privacy-first infrastructure, and the open-model stack as metaend. If you want to reach me, build on any of this, or tell me where it breaks, the door is here:

metaend on Quilibrium

More writing lives at paragraph.com/@metaend. And if you are curious how legible your own site is to AI agents, run it through metaend Grade.

Written by metaend.

]]>metaend@newsletter.paragraph.com (metaend)aiagentsompcli<![CDATA[cln: strip the tracking, keep the link]]>https://paragraph.com/@metaend/cln-strip-tracking-keep-the-link 0k6WY0a05ZAxvLAXz7EsThu, 18 Jun 2026 09:50:02 GMTEvery link you copy is a little dossier. Paste a product URL into a chat and you often ship along utm_source, fbclid, gclid, mc_eid: a trail of who sent what, from which campaign, to whom. None of it is needed for the page to load. All of it follows the click.

cln is a tiny command-line tool that removes that trail. You give it a URL, it strips the known tracking parameters, prints the clean version, and copies it to your clipboard. One word, one clean link.

$ cln 'https://example.com/page?utm_source=nl&utm_medium=email&id=42'
https://example.com/page?id=42
removed: utm_source, utm_medium

The cleaned URL goes to stdout (so it stays pipeable) and to your clipboard (with no stray trailing newline). Anything it stripped is reported on stderr as removed: …, so you can see exactly what was taken out.

Strip the tracking, keep the attribution

The interesting decision in cln is what it doesn't touch.

It removes tracking only: the whole UTM family (anything starting utm_), click IDs (fbclid, gclid, msclkid, igshid, …), email/campaign tokens (mc_eid, mkt_tok, _hsenc, …) and analytics/session junk (_ga, _gl, …).

It deliberately preserves affiliate and referral parameters like tag, ref, partner, irclickid, the Rakuten ran* set, and so on:

$ cln 'https://www.amazon.com/dp/B0XXXX?tag=metaend-21&utm_source=x'
https://www.amazon.com/dp/B0XXXX?tag=metaend-21
removed: utm_source

The tag=metaend-21 affiliate credit survives; the campaign tracker does not. This is intentional. A privacy tool that quietly rewrote or dropped affiliate tags would be doing the Honey thing, siphoning credit behind the user's back. cln never injects, alters, or substitutes attribution. It only ever preserves what's already there. And the default is conservative: anything not explicitly known to be tracking is kept, so a functional or revenue-bearing parameter is never stripped by accident.

Built small on purpose

cln is a single static Go binary with a deliberately minimal trust surface:

The realistic failure mode for a tool like this is parsing a URL wrong (leaking a param that should be stripped, or mangling a working link), so kept-parameter order and original encoding are preserved exactly, and the behavior is pinned by a table-driven test suite.

Using it

cln <url>     # clean the given URL
cln           # read the URL from the clipboard, clean it, write the result back
cln -h        # usage

Bare cln is the everyday path: copy a messy link from your browser, run cln, paste the clean one.

Install needs a clipboard backend for your display server (wl-clipboard on Wayland, xclip on X11) and a one-line build:

CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o cln .
cp cln ~/.local/bin/cln

…or just make install. The denylist of what counts as "tracking" is a single compiled-in extension point in clean.go: add a param if you decide it's pure tracking, rebuild, done. No config files, no remote rulesets, no daemon.

Source

gourl-cleaner on gitworkshop.dev - MIT licensed.


Built by metaend - verifiable, privacy-first infrastructure.

]]>metaend@newsletter.paragraph.com (metaend)privacygocligourl-cleanercln<![CDATA[I Built Analytics That Cannot See You]]>https://paragraph.com/@metaend/analytics-that-cannot-see-you KodQAo51fOYllHMvGmm5Mon, 08 Jun 2026 10:40:46 GMTEvery analytics tool makes you a promise. "We respect your privacy." "We don't sell your data." "GDPR compliant." The promise is the product, because underneath it, the tool is still doing the thing it always did: writing down a record of your visit and trusting whoever holds it to behave.

I did not want to make that promise. I wanted to build something where the promise is unnecessary, because the math makes individual visits unknowable in the first place. So I built astro-private-count, and it has been running in production on my own site since this week, with a public dashboard you can open right now.

The problem with "privacy-friendly" analytics

Give the privacy-respecting tools their due. They dropped cookies, they stopped shipping your data to ad networks, they anonymized IP addresses. That is real progress and I am glad it happened.

But look at what they still do. For every visit, they compute a signal: a cookie, or a salted hash of your IP address and your user agent. Even when that hash rotates every day, a per-visit record still exists on the server for that day. There is a row, and that row is about you. The privacy comes from a policy wrapped around that row: a retention window, a promise not to look too closely, a legal basis in a document nobody reads.

Policy is better than nothing. But policy can change, leak, get subpoenaed, or get quietly ignored after an acquisition. I wanted a design where there is simply no row about you to begin with. Where the privacy is a property of the system, not a sentence on a page.

Randomized response, a 60-year-old trick

The answer turned out to be older than the web. In 1965 a statistician named Stanley Warner wanted to survey people about embarrassing or illegal behavior and get honest answers. His insight: give every respondent deniability, and they will tell the truth, and you can still recover the group statistics.

The modern version, the foundation of what is now called local differential privacy, works like this. Suppose I want to count how many visits did some action, a yes or no question. Before any answer leaves your browser, your own device flips a weighted coin.

About three times out of four, it sends the true answer. The other one time in four, it sends a random answer, a fair coin flip with no relation to what you actually did.

Now think about what arrives at my server. A "yes" lands. Did that visit actually do the thing? Or did the coin say "lie" and the random flip happened to land on yes? I cannot tell. Nobody can tell. Every single visitor has plausible deniability baked in, before the data ever crosses the network.

So how do you get useful numbers out of noise?

This is the part that feels like magic but is just arithmetic. The individual answers are deniable, but the amount of noise is known exactly, because I chose it. So I can subtract it back out in aggregate.

If a thousand bits arrive and I know that a quarter of them are random coin flips averaging to half yes, I can solve for how many of the real answers must have been yes. The estimate is unbiased: on average it lands exactly on the true count. The error shrinks as traffic grows, roughly with the square root of the sample. At a few hundred reports it is within a few percent. At ten thousand it is within two.

The trade is simple and honest. I give up the last sliver of precision, especially on low-traffic days, and in exchange no individual visit is ever recoverable, even by me, even with the raw database in hand. For "are people using this, is the funnel converting," that trade is not close. Privacy wins easily.

There is one detail I want to flag because it bit me and it is the kind of thing that quietly ruins these systems. There are two flavors of randomized response. In one, a "lie" sends the opposite of the truth. In mine, a "lie" sends a fresh random bit. They look almost identical, but they need different formulas to de-bias, and the denominators differ. Use the wrong one and every number you report is off by about fifty percent while looking perfectly plausible. I have a test that guards that exact line of math so a future change cannot silently reintroduce the bug.

What it actually stores

Two integers per event per day. How many reports arrived, and how many of them were a one. That is the entire database.

No cookies. No IP addresses. No user agents. No identifiers. No fingerprints. There is no table that represents a person, which means there is nothing to leak in a breach, nothing to hand over under a subpoena, nothing to de-anonymize three years from now when somebody gets clever. You cannot lose what you never collected.

And because nothing is stored on your device, the usual cookie-consent banner is not even legally required. I ship a short, honest notice instead. No modal, no "we value your privacy" dark pattern, no reject-all maze. Just a plain sentence telling you what is happening.

I publish the epsilon

There is one number that controls the whole privacy guarantee. It is called epsilon, and it sets how often the coin tells the truth. Lower epsilon means more noise and stronger deniability. Higher means cleaner numbers and weaker privacy.

Most systems that use differential privacy keep that number quiet, if they use it at all. I put it directly on the public dashboard. Anyone can see exactly how strong the guarantee is, in the open, and check the de-biased totals for themselves. A privacy claim you can audit is worth more than one you have to trust.

What this is, and what it is not

I am careful here, because the whole point of the project is that the claim is exact.

This is local differential privacy. It is a real, mathematical guarantee that the server learns totals and nothing about any individual. It is tunable and it is auditable.

It is not a zero-knowledge proof. Those are a different cryptographic tool for a different job, and bolting one onto a pageview counter would be theater. I will not call this ZK, because it is not.

And it does not anonymize your network connection. Your browser still opens a connection to my server, so my edge necessarily sees that some address connected at some time. I never log it, never store it, never build anything from it, but I cannot pretend the connection is invisible. True network-level privacy needs something like a mixnet in front of the client, and that is out of scope for an analytics library. Anything else would be overselling, and overselling is exactly what I built this to get away from.

Why I actually built it

This came out of a different project, an agent-readiness grader that scores websites on how well they serve AI agents, including how they handle privacy and data. I was about to add analytics to it, and every option on the table was something I would have docked points for if I found it on someone else's site.

That felt wrong enough to stop me. If I am going to hold the rest of the web to a standard, the tool doing the grading has to clear that same bar first. So the analytics had to be the kind I would give an A. That constraint is the whole reason this exists, and it is why it is open source: the standard only means something if anyone can use it and check it.

It is released under AGPL-3.0, runs on Bun, and installs as a normal Astro integration. Add it to your config, call track(), open the dashboard. That is the whole setup.

Privacy should be a property of the system, not a promise on a page. This is my attempt to build it that way, and to prove it by running it in the open.

You can see it counting live, epsilon and all, at metaend-grade.fly.dev/stats. The source is at gitworkshop.dev/ngmi@zaps.lol/astro-private-count, AGPL-3.0. Take it, run it, check the math.

metaend

]]>metaend@newsletter.paragraph.com (metaend)privacysecopsastro<![CDATA[The Agent Credential Problem Just Got a Real Answer]]>https://paragraph.com/@metaend/agent-credential-problem-just-got-a-real-answer GeDu9GNPhwYUrO6whkxeSun, 31 May 2026 17:11:06 GMTFor the last two years, the way most people have handed credentials to an AI agent has been embarrassing. You paste a password into a prompt. You drop an API key into a plaintext config file the agent can read. You hand over a token scoped to your entire account because scoping it properly was too much work. Then you hope nothing in the agent's context window ever leaks, gets logged by a third party, or gets hijacked by a prompt injection buried in a web page the agent happened to parse.

This is not a niche concern. A McKinsey survey found that 62% of companies are experimenting with AI agents but only 23% are actually scaling them, and security is one of the main reasons the gap is that wide. The capability is there. The trust layer is not.

Last week Proton Pass shipped something that closes a real part of that gap: AI access tokens. I have spent enough time building tooling around agent workflows to recognize when a design is right, and this one is right.

Click Here to Get It

What they actually built

Instead of giving an agent your credentials, you give it a token. The token is read-only, so the agent can use a login but cannot create, edit, or delete anything in your vault. It is scoped to specific vaults, so the agent sees only the items you assigned to it and nothing else. You set an expiration anywhere from one hour to one year, and you can revoke it instantly the moment something looks wrong.

The detail I like most is the mandatory justification. Every single time an agent reaches for a credential, it has to state a reason, and that reason lands in an audit log alongside the access event. So you do not just get a record that a login was used. You get a record of why the agent claims it needed it. That is the difference between a log you glance at and a log you can actually reason about after the fact.

All of it sits on Proton's end-to-end encryption, which means the underlying secrets stay encrypted and only you hold the keys. The token is a controlled, observable window into your vault, not a copy of its contents.

The part developers will care about

The tokens are not limited to chat-style agents. They work with the Proton Pass CLI, which means you can wire them into your own scripts, automation, and CI/CD pipelines using the same least-privilege, audited model. If you are the kind of person who has a cron job that needs one specific credential and you have been doing unspeakable things with environment variables to make that happen, this is a cleaner path.

You create the token in settings, copy the setup instructions to your agent, and then just ask it to do work that touches the shared items. Setup is minutes, not an afternoon.

Why this matters beyond the feature

Here is the part worth sitting with. The right mental model for an agent is privileged software. It can act on your behalf, which means it can also make mistakes on your behalf or be turned against you by someone who manages to influence its inputs. You treat privileged software a specific way: you scope what it can touch, you log what it does, and you keep a kill switch within reach.

That is exactly the shape of what Proton shipped. Scope, log, revoke. It is the credential slice of a much larger agent security stack, and it is genuinely well executed.

It is also worth being honest about what one feature does and does not cover. Credential scoping and access logging are solved here. The other layers are still yours to build: sandboxing the execution environment so a compromised agent cannot reach beyond its task, enforcing policy on what actions are even permitted, and making your audit trail tamper-evident so a record cannot be quietly rewritten after an incident. Those problems do not disappear because your secrets are scoped. But a system that gets the credential layer this right is the kind of foundation worth building the rest on top of, instead of fighting against.

For the broad category of people who were avoiding agents entirely because handing over credentials felt reckless, the calculus just changed. You can give an agent real work, watch exactly what it does with your accounts, and shut it off in one click. That is a reasonable trade, and a week ago it mostly was not on the table.

If you want to try it

AI access tokens are included at no extra cost on Pass Plus, Pass Family, Pass Professional, Proton Unlimited, and Proton Workspace, so there is no separate add-on to buy. If you want to set up Proton Pass, you can do it here:

https://go.getproton.me/SH2fS

(That is a referral link. I would not point you at a credential model I thought was sloppy, and I am telling you it is a referral link so you can decide what to do with that.)

Build your agents like privileged software. Now at least the keys are under control.

metaend

]]>metaend@newsletter.paragraph.com (metaend)ai-agentsprotonagent-security<![CDATA[Your Website Has a Second Reader Now, and It Cannot See Your Homepage]]>https://paragraph.com/@metaend/see-your-site-the-way-an-agent-sees-it 2YUlGCnRtnD6RAssjBoLFri, 29 May 2026 10:13:54 GMTFor about thirty years the web had one audience: people. You designed for eyes, you optimized for search engines so more eyes would arrive, and that was the whole game. That game is ending. A second reader has shown up, and it does not work anything like the first one.

The second reader is an agent. It is the thing behind Claude, ChatGPT, Perplexity, and the growing population of autonomous clients that crawl, retrieve, compare, and increasingly buy on someone's behalf. When one of them arrives at your site, it does not admire your hero image or your animated gradient. It goes looking for machine-readable signals: a description it can ingest in one fetch, content it can request as JSON or Markdown instead of scraping out of HTML, structured data, an agent card, a way to pay. On most sites it finds none of that. So it does what any reader does when a page is unreadable. It gives up and moves on, and you never see the visit that did not convert.

I kept running into this while building agent tooling, so I built something to measure it. It is called metaend Grade, and it is live.

metaend-grade.fly.dev

What it does

You give it a URL. It probes the site the way an agent would, then hands back a grade from A to F across roughly twenty scored checks plus a set of informational ones. The categories are the things that actually determine whether an agent can use your site:

Discovery files like llms.txt and a sane robots policy that does not accidentally wall off the very agents you want. Content negotiation, which is whether the same URL can return clean JSON or Markdown when an agent asks for it with an Accept header, instead of forcing it to parse a page built for humans. Structured data and metadata. A2A agent cards and the newer ERC-8004 trustless-agent signals. Identity surfaces like DID, WebFinger, and Nostr. And machine-payment support, because an agent that cannot pay you is an agent that cannot transact with you.

Each failed check comes with a plain-language fix. The report is not a vanity score. It is a to-do list.

It practices what it grades

The part I am most pleased with is that the scanner is itself agent-ready. It serves its own llms.txt, agents.txt, agent card, security.txt, sitemap, and JSON-LD, and it content-negotiates its own root so an agent asking for JSON gets JSON. You can call it directly without touching the UI at all. A single GET to its compact endpoint returns a graded report as JSON, which means another agent can scan a site, read the result, and act on it in one move. Sending an Accept: application/json header to any page skips the human interface entirely.

If you are going to grade the web on agent-readiness, the tool doing the grading had better pass its own test.

Built to be shared and acted on

A grade is more fun when you can show it off, and more useful when you can do something with it. So a result gives you a few ways out.

You can share a grade straight to Bluesky, Farcaster, or Nostr, and the link unfurls with a dynamic card that bakes in the host, the letter grade, and the score. Both the result pages and the homepage generate their own cards on the fly, so a shared link actually looks like something instead of a blank rectangle. I also fixed an early bug where the card could be hijacked by the scanned domain's own metadata, so the card you share always reflects metaend Grade's reading of the site, not whatever the site wanted to claim about itself.

For the builders, there is a one-click Copy JSON button that hands you the full report as structured data, ready to paste into a coding CLI or feed to your own agent so it can work through the fixes. And if the tool saves you time, there is a quiet donate link, no pressure attached.

Paying for it, the way agents pay

Scanning is not free to run. Each scan fires a couple of dozen probes against the target. So there is a free daily allowance, and beyond that, scans are paid. The interesting part is how.

The paid path uses x402, the open standard that revives the long-dormant HTTP 402 Payment Required status code and turns it into a real payment step. An agent calls the endpoint, gets a 402 back with machine-readable terms, signs a payment locally, and retries. No account, no card form, no API key dance. The whole exchange happens in HTTP, which is exactly what an autonomous client needs.

This is not a testnet demo or a mock. Payments are real EIP-3009 wallet transfers in USDC, settled on-chain on Base and Arbitrum and confirmed before anything is served. On the backend it verifies and settles through a neutral, self-hostable facilitator, records every settlement, rejects replays, and fails closed, so a scan only runs once the money has actually moved.

There is also a small per-site cooldown on the free path, a ten-minute window per scanned host to keep things from being hammered. Paying skips it. If you need to re-scan the same site right now rather than in ten minutes, a payment takes you straight through.

Humans are not left out of any of this. If you burn through the free daily allowance, or you just do not want to wait out the cooldown, you can top up directly with the same Base and Arbitrum settlement the agents use, wrapped in a button for people who would rather click than hand-sign a payload.

How to use it

Run your own site first. Most people are surprised, and not in a good way. A D or an F is common right now, not because anyone did anything wrong, but because the agent-readiness layer simply was not part of how we built sites until very recently. The fixes are mostly small and static: a file here, a header there, a block of structured data. Then re-scan and watch the grade climb. Run your competitors too, since seeing where everyone sits is the fastest way to understand how early this all is.

The agentic web is being assembled right now, in public, while most of the web is still built for an audience that is no longer the only one reading. Closing that gap is cheap if you start now and awkward if you wait until your category is full of A grades and you are the lone F.


Two tools I actually use for this kind of work

I do not pad posts with affiliate links, so here are exactly two, both things I use daily and both relevant to anyone building in this space.

Running the models. Almost all of my agent and tooling work runs on open models, and the cheapest, least annoying way I have found to reach them is Nano-GPT: one key, pay as you go, no subscription lock-in, full access to GLM, Qwen, Kimi, MiniMax and others through a single OpenAI-compatible endpoint.

Reach every major open model through one pay-as-you-go key, no subscription. Try Nano-GPT →

Giving agents credentials without losing sleep. The other half of this work is letting an agent act on your accounts without handing it the keys to everything, and Proton Pass recently went agentic: read-only, vault-scoped, expiring access tokens, with a mandatory justification logged on every access and instant revocation.

Hand agents read-only, scoped, revocable credentials instead of pasting passwords into prompts. Set up Proton Pass →

Both links above are referral links. I am telling you that plainly so you can decide what to do with it. I would not point you at either if I did not run them myself.


Go scan your site. Tell me what grade you got.

metaend-grade.fly.dev

metaend

]]>metaend@newsletter.paragraph.com (metaend)aiagenticmetaendllmerc-8004<![CDATA[Building a personal AI assistant that I'd actually trust with my life]]>https://paragraph.com/@metaend/personal-ai-assistant-id-trust-with-my-life xqXe6MU4XS0un0G4yWtWWed, 27 May 2026 08:35:30 GMTI run a lot of projects in parallel. Family, company, a relocation, training, the usual chaos. The pile kept growing past what I could hold in my head, so I built an always-on assistant to externalize the load. Calendar, todos, project context, reminders, email triage, all behind a single chat I can reach from any device. Nothing exotic about the goal. The interesting part is what it took to build one I'd actually trust with the contents of my real life.

This post is the architecture and the security choices. It's opinionated and specific. If you're considering the same thing, the trade-offs below will save you a few weekends.

Threat model, written down

Before tooling: write down what you're protecting against. Mine, roughly:

That last one matters because the assistant's context lives in a git repo I sync across devices. The repo is the brain.

The stack

I picked the Hermes Agent framework from NousResearch. Open source, batteries included (tool use, memory, skills, hooks), self-hostable, well documented. It's the only framework I found where "run a real personal agent on my own hardware" was an obvious target rather than an afterthought.

Around it:

The host is a Linux box with LUKS full-disk encryption that stays on 24/7.

Where the privacy floor actually is

Most of the work in this build was making the agent useful AND making sure I wouldn't be embarrassed if any one component got rooted. A few specific decisions that did the heavy lifting:

Inference: ZDR by default, opt-in alternate

The default model is zai-org/glm-5.1:wafer, routed through nano-gpt's Wafer tier. The :wafer suffix is the part that matters: it's their zero-data-retention path. Requests are processed and dropped. No training, no logs of prompt content. I verified this against their published policy and I plan to re-verify periodically rather than take it on faith.

GLM-5.1 itself is capable enough for everything I throw at the assistant in practice: planning a week, summarizing an email thread, drafting a follow-up, querying my todos, deciding which project a new task belongs to. It's not the best model on every benchmark, but it's the best ZDR model I have access to with a price that lets me run an always-on bot without anxiety about cost.

If you want to try the same stack: nano-gpt referral link.

The opt-in alternate route is uomirouter on Qwen3.6-27B. Less private (their gateway sees full request content), but fast and cheap, and useful when I'm doing throwaway work where the privacy posture doesn't matter. I wired it as a slash command (/uomi) that switches the session for one cycle, with a loud banner on every reply reminding me the call is going through a different path. Default never moves; the alternate is one tap away when I want it, and one /reset away when I don't.

Uomi gives free credit on signup and a bit more via referral link ($3.50 free up front, $5 on top through the link). Enough to validate the route without committing.

Sandboxing: rootless Podman, not Docker

Hermes can use either Docker or Podman as the runtime for its terminal tool. I went with rootless Podman for three reasons:

  1. No root daemon. Docker runs a privileged daemon by default. Podman doesn't. The agent's containers spawn under my user account without any process running as root. If the daemon is the most attractive attack surface in a typical Docker setup, removing it is the right move.

  2. User-namespace isolation by default. Rootless Podman maps the container's UID 0 to my user's subuid range, not actual root. A container escape gets you my user, not the host's root. You wanted that anyway, but here it's the default rather than something you opt in to.

  3. Drop-in compatibility. Podman accepts the same docker run flag set, so all the security hardening (--cap-drop ALL, --security-opt no-new-privileges, the tmpfs mounts, the pids limit) just works. Switching the runtime didn't change the agent's behavior at all.

Inside the sandbox: only the workspace repo is mounted, read-write, with --userns=keep-id so file ownership comes through correctly. None of ~/.ssh, none of ~/.config, none of my other dev repos. The agent can git status and grep the workspace, but it cannot see the SSH key it'd theoretically need to push, the OAuth tokens for calendar, or any other secret on the host. The container even runs without network for most operations; outbound calls go via specific tools the agent has to explicitly invoke.

The trade-off: the agent can't run git push from inside the sandbox (no SSH key visible). Solved by a small post-turn hook on the host that auto-commits the agent's workspace edits locally; pushes remain a manual action I run. Reversible work the agent does itself, irreversible work I gate.

Secret + PII redaction in the agent loop

Hermes ships with two switches I leaned into:

Both default to off; turn them on early.

Git-crypt for the brain

The workspace repo holds my real life: open projects, partner's name, kids' schedules, what I'm building for work. I push it to Codeberg for backup and cross-device sync, but the parts that matter are encrypted with git-crypt before they leave the box. The remote sees ciphertext for SOUL.md, config.yaml, todos.md, projects/**, the skill files, and the protocol files. Filenames + commit graph are visible (they're inherent to git), but content is not.

The git-crypt key lives in ~/.config/git-crypt/keys/ at chmod 600, backed up to a password manager as a base64 secure note and to an offline encrypted medium. Losing the key means losing the encrypted history with no recovery, so back it up before your first push, not after.

Custom skills + hooks for behavior shaping

Hermes lets you ship custom skills (instructions the agent loads on demand based on intent match) and lifecycle hooks (Python handlers that fire on events like session-start, session-end, agent-end). I wrote three skills early because the agent kept misrouting project briefs into its memory tool instead of the project files:

And one hook (workspace-git-sync) that does the boring stewardship: pull the workspace on session start, auto-commit on agent end if the agent left anything uncommitted, never push (that stays manual). The result is that the brain stays current across devices without me thinking about it.

What's deliberately absent

The pattern is: enable when there's a concrete need that the current setup can't meet, not because it's an option in the docs.

What I'd tell someone starting today

  1. Write the threat model first. A page is enough. Decide what's acceptable and what isn't BEFORE you start picking tools, because the tool choices are mostly downstream of those decisions.

  2. Pick ZDR inference and stick to it. It limits your model menu, but not by much. GLM-5.1 on Wafer or one of the other ZDR routes is enough for any assistant-shaped task.

  3. Rootless Podman, not Docker. Same flags, fewer privileged surfaces.

  4. Encrypt the brain. git-crypt is twenty lines of setup and it means the contents of your repo are unreadable to the host you're syncing through.

  5. Auto-commit, never auto-push. Lets the agent maintain itself without ever doing something you can't quickly undo.

  6. Skills over prompt-hacks. When the agent does the wrong thing, write a small skill that targets the intent. Lasts longer than reprompting.

  7. Use the alternate-provider escape hatch. Have a fast/cheap route ready for the throwaway work; keep the ZDR route as the default. Don't mix them by accident.

Links one more time:

Both referral links if you want to support the writeup. Both products I actually use.


metaend

]]>metaend@newsletter.paragraph.com (metaend)agentsaiprivacyhermessecurity<![CDATA[jsonfix: a JSON repair API your agent pays for itself]]>https://paragraph.com/@metaend/jsonfix-pay-per-call-json-repair NSxeNzEzLBaNLdY8kic1Mon, 25 May 2026 12:59:09 GMT_One HTTP endpoint. $0.01 a call. No signup, no API key, no account. Built on x402 for agents that need to fix broken JSON and just want to get on with it._

Every LLM pipeline I have ever touched has the same quiet bug. Somewhere, a model hands back JSON that does not parse. A trailing comma. Single quotes instead of double. A response wrapped in a markdown code fence. An object truncated mid-key because the generation hit a token limit. So you write a cleanup function. Then a slightly bigger one. Then you bolt on a retry. Then you start the next project and write the whole thing again.

jsonfix is that cleanup function, extracted, hardened, and put behind a single paid endpoint so nobody has to write it a fourth time.

What it does

You POST malformed JSON. You get valid JSON back.

curl -X POST https://ngmi--b088743456a711f1b5c3ee650bb23af1.web.val.run/repair \
  -H "Content-Type: application/json" \
  -d '{"input": "{name: \"Alice\", age: 30,}"}'

Optionally you pass a JSON Schema, and the output is conformed to it: correct types, required fields present, values coerced where the intent is unambiguous. Send a field as the string "30" with a schema that expects a number, and you get the integer 30 back. That coercion is the difference between a real tool and a thin wrapper around JSON.parse.

Two stages, because most JSON is only a little bit broken

jsonfix repairs in two stages, and the order matters.

First, a deterministic parser. It fixes the boring 90 percent instantly: trailing commas, unquoted keys, single quotes, comments, stray code fences. No model involved, no latency, nearly free to run.

Only when the bytes are genuinely too broken, truncated, or failing their schema does the request fall through to the second stage: a Wafer-routed Qwen model that reconstructs the intended structure. Most calls never reach it. That is what keeps the endpoint fast and the price honest. You are not paying for an LLM to add a missing comma.

The part that is actually new

Here is the bit I care about. jsonfix has no signup page. No dashboard. No API key to rotate. No "contact sales."

It speaks x402. An agent calls the endpoint, gets back an HTTP 402 Payment Required with the terms attached, its wallet pays one cent in USDC on Base, and it retries the exact same request. The payment is gasless for the caller. The entire negotiation is two HTTP round trips and zero human steps.

This matters because the signup form is the last thing standing in an agent's way. An agent can reason, plan, write code, and call tools, and then it walks straight into a page that needs a human with a credit card. x402 deletes that page. jsonfix is a small, useful proof that the pattern works in production: an agent that produces broken JSON can buy its own repair, mid-task, without ever asking you.

Zero data retention

The JSON you send is processed in memory and discarded with the response. Nothing is stored. Nothing is logged. Inference is routed through Wafer, a fully zero-data-retention provider. If the JSON you are repairing carries anything sensitive, it does not stick around after the reply leaves the server.

The stack

It is a deliberately small stack. Val Town for hosting, so the whole thing is one file with instant deploys. Hono and Deno underneath. Payments settle through an open, neutral x402 facilitator rather than a big-platform one, because the entire point of an open payment standard is that charging a cent should not require permission from a gatekeeper. Inference is Qwen, routed via NanoGPT's Wafer option.

Idea to shipped took a weekend. That is sort of the pitch for this whole class of thing.

Try it

The endpoint is live on Base mainnet right now. Point any x402-capable client at it and it just works. If you want to inspect before you pay, there is a free probe: a plain GET to /openapi.json returns the full machine-readable spec.

If you are wiring up an agent, the one-line instruction below goes straight into your system prompt:

When you produce JSON that fails to parse or fails its schema, POST
{ input, schema?, instructions? } to
https://ngmi--b088743456a711f1b5c3ee650bb23af1.web.val.run/repair
(paid: $0.01 USDC via x402 on Base mainnet) and use the returned data.

That is the whole product. One endpoint, one cent, no humans required.

The val is named ngmi. We will see.

]]>metaend@newsletter.paragraph.com (metaend)x402aillmagentic<![CDATA[Markdown In, HTML Out]]>https://paragraph.com/@metaend/markdown-in-html-out NIhDjBmzFe4SZOW5CgvlWed, 20 May 2026 07:28:49 GMT_Why your specs are unread and what to do about it_

A few weeks ago Thariq Shihipar from the Claude Code team gave a talk that named something I had been doing badly. The thesis fits in one sentence:

Markdown is the right format for instructing an LLM. It is the wrong format for whatever the LLM produces for you to read.

CLAUDE.md, AGENTS.md, SKILL.md are markdown for a reason. Terse. Easy to grep. Easy to keep token-cheap. The model reads them well.

But the output you ask the model for, a plan, a PRD, an architecture writeup, a status doc, is read by a human, not a model. And past a hundred lines, nobody reads markdown. It is a wall of text. There are no anchors, no visual hierarchy beyond ATX headings, no way to collapse a section, no embedded mockup, no working example, no diagram, no diff highlight. Open one of your 1k-line specs and ask honestly when you last scrolled past line 200.

The fix is to have the model emit HTML instead.

On the open-model side, nano-gpt is the cheapest way to try GLM, Qwen, Kimi, and MiniMax through one API key before committing to any of them. Pay per token, no monthly minimum. Check it out!

What HTML buys you

A single self-contained .html file rendered offline gives you:

The same artifact serves you (the author), your team (review), your stakeholders (sales, regulators, investors), and your future agent (next session context). One file, four audiences.

The pattern

project/
├── src/                   # code
├── dist/                  # generated HTML artifacts
│   ├── index.html         # landing, links, summary
│   ├── plan.html          # scope, milestones, risks
│   ├── architecture.html  # components, data flow, threats
│   ├── design.html        # tokens, type, components
│   └── status.html        # blockers, next steps
├── AGENTS.md              # token-compressed agent directives
└── html.prompt            # paste-ready agent spec for the artifact set

Markdown for the instructions. HTML for the outputs. The agent does the translation.

html.prompt

I packaged this as a paste-ready file you can drop in any project. It tells the agent what artifacts to build, how to constrain them, and what triggers regeneration.

curl -O https://gitworkshop.dev/ngmi@zaps.lol/relay.ngit.dev/dotprompt/html.prompt

claude "read html.prompt, generate dist/ artifact set for this project"
# or
opencode "read html.prompt, generate dist/ artifact set for this project"

Constraints baked in:

Repo: https://gitworkshop.dev/ngmi@zaps.lol/relay.ngit.dev/dotprompt. MIT. One file. Fork and adapt.

Where this lands

The dist/ directory becomes the canonical spec of the project. It commits with the code. It deploys as a static site to GitHub Pages, Cloudflare, IPFS, or tangled.org. It is what you link to when someone asks for the PRD.

For me this replaced three patterns at once:

  1. Long markdown PRDs nobody reads past the TOC

  2. Stale Notion pages that drift from the code

  3. Figma mockups that exist outside the agent's context

A single html.prompt plus a dist/ is lighter than all three and lives in the repo where the code does.

A note on which agent

This works with any agent that reads files and writes files. Claude Code produces the cleanest HTML out of the box. Open models via OpenCode (GLM-4.6, Qwen3, Kimi) produce passable HTML if you point them at the embedded skeleton in html.prompt as a reference. For client-facing artifacts the quality delta matters and I route those through Claude. For internal status docs and architecture writeups any of them is fine.

The prompt is the source. Edit it. Add artifacts. Tighten privacy rules. Shift the regen triggers. It is one file.

Why this matters past the productivity angle

Specs as living HTML artifacts collapse the gap between code, documentation, and presentation. The same artifact a developer reads in dist/plan.html is the one a regulator reads at yourproject.org/plan. The same artifact the agent loads as context next session is the one a potential customer sees in a pitch. You stop maintaining three derivatives of the same information.

For anyone shipping AI-adjacent infrastructure, where compliance posture, threat model, and audit trail are part of the product, this is not a productivity hack. It is a way to make the spec the deliverable.


Credit where it belongs. Thariq Shihipar's talk is the source of the thesis. html.prompt is the operationalization I now use across every project.

If you fork it or build something on top, drop me a line.

— metaend

]]>metaend@newsletter.paragraph.com (metaend)aiagentsdotpromptllm<![CDATA[Why We Ended Up Building on Aztec]]>https://paragraph.com/@metaend/why-we-ended-up-building-on-aztec VnEdqsrhKvahZQUkSdyOWed, 22 Apr 2026 10:28:15 GMT

A quiet shift is happening between AI and regulation, and most people in either field are underestimating it.

On the AI side, models now routinely touch data with real privacy and legal weight: building operations, emissions, supply chains, medical flows, financial positions, biometric evidence. The more useful a model gets, the more of that data it ingests and the more it derives inferences from.

On the regulatory side, jurisdictions have started demanding proof, not claims, that specific thresholds are being met. Carbon intensity bands. Fair-lending scores. Model-bias audits. Human-oversight attestations. The direction is clear: if an AI system is making decisions about you or your asset, someone wants a verifiable record that the decision was legitimate.

The tension is obvious. You cannot publish the raw data. It is commercially sensitive, personally identifying, or legally restricted. But you also cannot just trust a certificate PDF signed by "TrustMeBro Ltd." Regulators want cryptographic teeth. Users want privacy. Both sides are, in principle, right.

This is a very old cryptographic wish list with a very new urgency.

The shape of the problem

Strip away the domain specifics and you end up wanting something like:

This is a ZK-shaped problem. More specifically, a private smart contract shaped problem: you need state, access control, something like events, and the private and public halves must be part of the same computation.

Why not the obvious options

We looked at what was available.

ZK rollups on Ethereum are mostly optimised for scaling, not privacy. State is public by default. You can bolt on ZK-SNARK flows for individual actions, but building a stateful privacy-preserving contract on top of a transparent rollup means reinventing half of what a privacy L1 gives you for free.

Mixers and payment-privacy protocols handle value transfers well but do not give you general-purpose contract state. Wrong shape for attestation.

Fully homomorphic approaches are philosophically beautiful but operationally painful for anything beyond toy workloads. Performance and tooling are not there yet.

Off-chain ZK with on-chain verifier (the "SNARK library plus verifier contract on Ethereum" pattern) works, but every piece of state you want to keep private still has to live off-chain in a trusted service. You end up rebuilding a PXE of your own, badly.

What we needed was a chain where private state is a first-class primitive, not an afterthought, and where a single contract can hold both private notes and public state and hand off between them cleanly.

How Aztec fits

Aztec splits every contract into two execution layers. Private functions run in the user's own browser or client, produce a ZK proof, and emit encrypted notes that only the intended recipient can decrypt. Public functions run on the rollup like a normal L2 and see only the aggregate, non-sensitive state.

In practice this maps directly onto the attestation problem:

The chain sees a public summary, a verifier address, a timestamp. The world sees enough to trust the system without seeing anything sensitive. The regulator can demand the underlying data directly from the owner and verify it matches the on-chain commitment.

That is the shape we wanted. Aztec is the first mainstream system where it is not a research project to build.

The missing half

There is a matching problem upstream. The data reaching the private note does not appear from nowhere. It passes through a pipeline: meters, parsers, AI agents, API pulls. If that pipeline is opaque, the on-chain rigor is undercut by an ungovernable off-chain half.

We run that layer in WasmBox, a capability-sandboxed WASM tool launcher we built for exactly this kind of workload. Every execution runs against explicit syscall-level grants, produces an append-only JSONL transcript, and signs the transcript hash with Ed25519. That signed hash becomes the raw_data_hash committed in the Aztec note. On-chain you see the commitment. The auditor, when invited, reconstructs the full execution log the commitment points to.

The two halves answer different questions. Aztec answers "what can the public verifiably know about private state?" WasmBox answers "what can we verifiably say about how that state was produced?" Together they give you an honest claim about private data, end to end.

What it actually feels like to build on

We implemented a real contract, around 300 lines of Noir, covering the full flow: admin, verifier registry, private attestation with a private-to-public teardown, public read endpoints, and selective disclosure to auditors. Then we migrated it to the just-released v4.2.0-aztecnr-rc.2. A few honest observations.

The mental model is good. Once the private/public split clicks, writing privacy-preserving contracts stops feeling exotic and starts feeling ordinary. The syntax is Noir: more Rust-like than Solidity-like, with strong types and a real compiler.

The tooling is young. The v4.2 release is a near-total API rewrite compared to six months ago. That is healthy for a young system but brutal on documentation; half the tutorials you find online are already wrong. Expect to read the sample contracts in the monorepo more than the docs.

The local sandbox works, and that is a bigger deal than it sounds. You can boot an L1, the rollup, and the PXE in one command, deploy a contract, and exercise the full private-to-public flow on a laptop in under a minute. That single fact is the best predictor of whether a chain is real to develop on.

Production is still ahead. A disclosed critical vulnerability in the proving system (mid-March this year) means real mainnet deployments with real data should wait for the v5 release, targeted for mid-2026. Testnet and sandbox are fine for building now.

What we are sitting with

The thesis is simple. AI is about to make "honest claims about private data" the bottleneck for a lot of real-world compliance. The chains that natively support private state and selective disclosure are going to be where that workload lives. Aztec is the most coherent attempt we have seen at that primitive, and it is far enough along to build against seriously.

We are not betting the company on it. We are doing what you should do with any young infrastructure: building something real on it, learning what it can and cannot hold, keeping our options open. If a better shape of the problem appears, we move. If v5 ships on schedule and holds up under real traffic, we stay.

Either way, the underlying question, how do you prove something about private data without revealing it?, is only going to get louder from here.


Disclosure: I do not hold AZTEC tokens and have no financial position in the Aztec Network. This work is pure research and development. The observations above reflect what we found while building; they are not investment advice, not a partnership announcement, and not a prediction about token price or timing. WasmBox is our own product and I have a commercial interest in its adoption. If any of the above changes I will say so.

]]>metaend@newsletter.paragraph.com (metaend)aztecaizeroknowledge<![CDATA[Pretext vs DOM Reflow: Real Benchmarks for Streaming AI Interfaces]]>https://paragraph.com/@metaend/pretext-vs-dom-reflow-streaming-benchmarks vrqrZc7Xg6cgDO4LINEbWed, 15 Apr 2026 09:35:55 GMTEvery token your AI streams triggers a question: how tall is this text now? The browser needs to know for auto-scroll, layout, overflow detection. The traditional answer is getBoundingClientRect(). The cost of that answer is a forced synchronous reflow.

Cheng Lou's Pretext (@chenglou/pretext on npm) sidesteps the DOM entirely. Pure JavaScript text measurement using canvas font metrics and arithmetic. No reflow. No layout thrashing. ~15KB gzipped, zero dependencies.

I benchmarked both approaches across 5 scenarios, 1000 iterations each, measuring prepare() + layout() against getBoundingClientRect() on a real element.

Results

| Scenario | Chars | DOM Reflow | Pretext | Speedup | | Short (1 token) | 4 | 0.024ms | 0.011ms | 2x | | Word (5 tokens) | 22 | 0.026ms | 0.023ms | 1x | | Sentence | 75 | 0.032ms | 0.030ms | 1x | | Paragraph | 235 | 0.089ms | 0.052ms | 2x | | Streaming (growing) | 356 | 0.122ms | 0.070ms | 2x |

The layout() function alone, called on an already-prepared handle, averaged 0.00052ms. That is the cached hot path.

Why This Matters for Streaming

Modern AI interfaces stream responses token by token. Gemma 4, GPT-5, Claude -- they all support SSE streaming. A typical response is 100-300 tokens arriving at roughly 20 tokens per second.

Each token changes the text content. Something needs to measure the new height to decide: should I auto-scroll? Did the bubble grow past the viewport?

At 20 tokens/sec with the longest benchmark text (356 chars):

| Method | Cost/sec | % of 16.67ms frame budget | | DOM reflow | 2.45ms | 14.7% | | Pretext (prepare + layout) | 1.40ms | 8.4% | | Pretext layout() only, rAF throttled | 0.06ms | 0.36% |

14.7% of your frame budget spent on text measurement is not catastrophic on Chrome. On Safari it gets worse. On a 2020 iPhone SE running Safari, DOM reflow costs scale significantly higher.

The optimal strategy: call prepare() when the text content changes (new token arrives), but throttle layout() calls to requestAnimationFrame. Since layout() operates on the cached prepared handle, you only pay 0.00052ms per frame for height checks. That is 0.36% of frame budget. Effectively free.

The Streaming Pattern

import { prepare, layout } from '@chenglou/pretext'

let prepared = null
let lastText = ''

function onToken(token) {
  buffer += token
  // prepare() on content change
  prepared = prepare(buffer, '14px Inter')
}

function checkHeight() {
  if (!prepared) return
  const { height } = layout(prepared, bubbleMaxWidth, 20)
  if (height > containerHeight) scrollToBottom()
  requestAnimationFrame(checkHeight)
}

requestAnimationFrame(checkHeight)

prepare() runs per token. layout() runs per frame. The two concerns are decoupled. Text segmentation and canvas measurement happen when content changes. Height calculation happens when the browser is ready to paint.

What Pretext Is Not

Pretext is not a rendering engine. It does not draw text. It does not replace CSS. It answers one question: given this text, this font, and this container width, how many lines and what height?

That question comes up more often than most developers realize:

Bundle Cost

~15KB gzipped, zero dependencies. For context, Oat UI (the CSS framework) is ~8KB. Adding Pretext roughly doubles your UI layer weight. Whether that trade-off makes sense depends on how often you measure text.

For a static landing page: skip it. CSS handles everything.

For a streaming AI chat interface running 20 measurements per second: the 15KB pays for itself on the first streamed response.

Early Days Disclaimer

Pretext ( github.com/chenglou/pretext) is weeks old as of writing. v0.0.5 on npm. 43k GitHub stars and massive momentum, but this is pre-1.0 software. No formal security audit. No stability guarantees. The API surface could change. Cheng Lou is iterating fast, shipping breaking changes in patches.

For production use: pin your version, read the changelog before upgrading, and test against your specific fonts and text patterns. The library is pure computation with zero network access, so the risk surface is small. But treat it as you would any pre-1.0 dependency.

Practical Notes

Use named fonts.system-ui produces inaccurate measurements on macOS due to font substitution behavior. Specify "Inter", "Helvetica Neue", or whatever you load.

Cache prepared handles. For completed messages that will not change, call prepare() once and store the handle. Only call it again if content changes.

Throttle layout() to rAF. Decoupling measurement from token arrival avoids redundant work when multiple tokens arrive within a single frame.

Pretext uses canvas internally. The first prepare() call creates an offscreen canvas context. Subsequent calls reuse it. There is no visible canvas element.

Reproducing These Benchmarks

The test measures prepare() + layout() vs creating a DOM element, setting textContent, appending to document, calling getBoundingClientRect(), and removing the element. 1000 iterations per scenario, 5 scenarios, median values reported.

The streaming scenario simulates growing text by appending characters incrementally and measuring at each step, which mirrors real token-by-token SSE behavior.

Hardware and browser matter. Chrome is faster at DOM reflow than Safari. Mobile Safari is slower than desktop Safari. The 2x speedup I measured is a conservative baseline on a desktop browser. Mobile devices with slower layout engines will see larger gaps.


The benchmark code and data are available on request. I ran these while building a Spanish learning app with streaming AI conversation practice. Pretext handles auto-scroll during token streaming, adaptive flashcard font sizing, and chat bubble width calculation -- all without touching the DOM.


Tools Used in This Research

WasmBox -- If you care about sandboxed tool execution for AI agents, WasmBox is what I am building. WASI sandbox runtime with SHA-256 content-addressed verification. Nine tools shipped, more coming. MIT core, FSL-1.1-Apache-2.0 compliance layer. Repository at tangled.org/metaend.eth.xyz/wasmbox-cli.

NanoGPT -- The streaming benchmarks used Gemma 4 31B via NanoGPT's OpenAI-compatible API. $8/month flat for access to all major open source models (Qwen, Kimi, DeepSeek, Gemma, GLM) with full API and CLI usage. No per-token billing. Crypto and card accepted. The link above gives you 5% off.

]]>metaend@newsletter.paragraph.com (metaend)pretextdomresearchllmfrontend<![CDATA[A2H Proof: Your Agent Doesn't Know You're Human]]>https://paragraph.com/@metaend/a2h-proof XwDBr7MHLOazx3aUxaa7Mon, 06 Apr 2026 06:57:45 GMTEvery proof-of-humanity system built so far has the same architecture: a platform verifies a user. Tinder checks your photo. Discord makes you solve a CAPTCHA. World ID scans your iris. The platform collects the proof, stamps your profile "verified," and that's it.

The agent trusts the platform. The platform trusts the verification provider. The user trusts everyone involved won't leak their data or go down. It's transitive trust all the way down.

I'm calling this P2U -- Platform-to-User verification. And it's about to become dangerously insufficient.

The Problem With P2U

When an AI agent executes a financial transaction, moderates content, or processes a request on your behalf, it has no idea whether a human is actually on the other end. It has a session token. Maybe an OAuth flow. Maybe a cookie that says you logged in three hours ago.

None of that proves a living human is present right now, at the moment the agent is about to act. A session token doesn't have a pulse. An OAuth flow doesn't prove humanness -- it proves account access. Another agent can hold both.

This isn't a theoretical problem. The EU AI Act (Article 14, enforcement date August 2, 2026) requires high-risk AI systems to implement meaningful human oversight. The word "meaningful" is doing heavy lifting there. A login cookie from this morning is not meaningful oversight of a decision happening right now.

A2H: Flipping the Direction

A2H Proof -- Agent-to-Human Proof -- inverts the verification direction.

Instead of a platform checking a user at signup, the agent itself challenges the human at the decision boundary. The exact moment it's about to act. The human responds with a cryptographic proof generated on their own device. The agent verifies it in a sandboxed, auditable environment. No platform intermediary. No transitive trust.

The distinction:

P2U (Platform-to-User): Platform checks user at signup. Agent trusts platform's word. Trust is indirect and stale.

A2H (Agent-to-Human): Agent checks human at the point of action. Trust is direct, cryptographic, fresh, and auditable.

A2H doesn't replace P2U. Platforms should still verify users at onboarding. But when an AI agent is about to do something consequential -- approve a transaction, publish content, escalate a support case, execute a trade -- it should be able to independently confirm a human is in the loop. Not trust that someone else checked last Tuesday.

What A2H Looks Like

The flow is straightforward:

  1. Agent hits a decision that requires human oversight

  2. Agent generates a verification challenge (could be a QR code in a terminal, a push notification, a deep link)

  3. Human responds with a cryptographic proof of humanness generated on their own device

  4. Agent verifies the proof in a sandboxed, capability-bounded runtime

  5. Verified? Proceed. Not verified? Block, escalate, or retry.

The proof layer can be anything that provides verifiable uniqueness and humanness. World ID's Groth16 ZKPs are the most mature option today. Civic, Gitcoin Passport, and future decentralized identity protocols could serve the same role. The A2H pattern is provider-agnostic -- what matters is that the agent is the relying party, not the platform.

Why the Runtime Matters

If your verification logic runs inside your application runtime, you have a trust problem. Who's to say the verification wasn't spoofed? A compromised application could simply return {"verified": true} for every check and nobody would know.

A2H only works if the verification runs in an isolated, auditable environment. That means:

This is the architecture we're building with WasmBox -- WASI-based sandboxed tool execution where every binary is capability-bounded and SHA-verified. A2H Proof is one of the use cases this kind of runtime was designed for.

Where A2H Changes Things

Regulatory compliance. Article 14 of the EU AI Act requires human oversight for high-risk AI. A2H provides cryptographic proof of human presence at the decision boundary -- not a process document claiming a human was involved, but a verifiable receipt.

Agent-to-agent escalation. In multi-agent systems, how does an agent know that the "human supervisor" it's escalating to is actually human? A2H at the escalation boundary closes this gap.

High-value transactions. AI trading agents, payment processors, and approval workflows can gate execution behind A2H. Not "does this session have permission" but "is a human here right now authorizing this."

Content authenticity. A moderation agent can tag content as human-originated with cryptographic backing. Not a platform badge -- a ZKP that proves a unique human posted this, verifiable by anyone.

Sybil resistance. Any service where one-human-one-account matters (voting, airdrops, waitlists) can enforce it at the agent layer rather than the platform layer.

The Bigger Picture

We're entering a period where the majority of internet traffic will be agent-generated. Most API calls, most content, most transactions will originate from AI systems acting on behalf of humans -- or acting autonomously. The ability to distinguish "a human is here" from "a bot says a human is here" becomes a foundational primitive.

P2U verification was built for a world where humans operated computers directly. A2H is the verification model for a world where agents operate on behalf of humans, and the agents themselves need to know when a human is genuinely present.

The proof-of-humanity protocols exist. The sandboxed runtimes exist. The regulatory pressure exists. What's been missing is the pattern that connects them -- a name for the verification direction that actually matters in an agentic world.

That's A2H. Agent-to-Human Proof. The agent asks. The human proves. The math checks out.

]]>metaend@newsletter.paragraph.com (metaend)aiagenta2hp2u<![CDATA[Generative Engine Optimization in 2026: What Actually Works, What Doesn't, and Why You Should Care]]>https://paragraph.com/@metaend/generative-engine-optimization-research-2026 DOGDaFLGXoQlqzrV88ugTue, 24 Mar 2026 08:33:52 GMTThe way people find information online is shifting. AI search engines like ChatGPT, Perplexity, Google AI Overviews, and Claude now synthesize answers from multiple sources instead of returning a list of links. This changes the game for anyone who publishes content on the web. The emerging practice of optimizing for these AI engines has a name: Generative Engine Optimization, or GEO.

But here is the honest truth. GEO is a field where a handful of rigorous academic studies are surrounded by an ocean of speculation and premature commercialization. This post breaks down what the research actually shows, where the real opportunities lie, and where the hype falls apart.

Share

What is Generative Engine Optimization?

GEO is the practice of structuring and optimizing web content so that AI-powered search engines are more likely to cite, reference, or surface it in their generated responses. Traditional SEO targets a ranked list of blue links. GEO targets inclusion in a synthesized AI answer.

The term was coined by researchers at Princeton, Georgia Tech, the Allen Institute for AI, and IIT Delhi in a November 2023 paper that was later published at ACM SIGKDD 2024, one of the top conferences in data science. That paper remains the foundational controlled experiment in the field.

The distinction from SEO matters because AI engines work differently under the hood. They use a Retrieve-Augment-Generate (RAG) architecture: decompose a query into sub-queries, retrieve relevant text passages via semantic embedding, re-rank by relevance and authority, then synthesize a single answer with selective citations. They evaluate meaning, not keyword frequency. They pull fragments of pages, not whole pages. And they select sources probabilistically, meaning there is no stable "position 1" to chase.

Use the latest open source AI models for $8/month with full API and OpenCode CLI access — no per-token billing, no vendor lock-in. Get started on NanoGPT →

What the Research Actually Shows

The Princeton Study: The Only Controlled Experiment at Scale

The foundational GEO paper ( Aggarwal et al., KDD 2024) tested 9 optimization strategies across 10,000 queries using a custom benchmark called GEO-bench. The results were striking:

Adding citations and references to content produced the single largest visibility boost, up to 115% for sites that started at mid-ranked positions. Statistics and quantitative data delivered 22 to 40% improvements, with the strongest gains in law and government content. Expert quotations improved visibility by 37%, especially for opinion and historical topics. Fluency optimization, meaning clear and logically ordered prose, produced a consistent 15 to 30% gain that compounded with other techniques.

The most important negative finding: traditional keyword stuffing actively reduced visibility. This directly contradicts a core assumption inherited from SEO.

The researchers validated their results on Perplexity.ai, demonstrating 22 to 37% real-world improvements, which moved this beyond a purely synthetic benchmark.

AutoGEO: Automating the Process

A second major study from Carnegie Mellon ( Wu et al., accepted at ICLR 2026) introduced AutoGEO, a framework that uses frontier LLMs to automatically discover optimization rules, then trains compact models via reinforcement learning to apply them. AutoGEO achieved up to 50.99% improvement over the best manual baseline from the Princeton study. Crucially, it introduced Generative Engine Utility as a metric, measuring whether optimization degrades answer quality for end users.

C-SEO Bench: The Cold Water

The most critical counterpoint arrived in June 2025 from Puerto et al. Their C-SEO Bench study found that most current conversational SEO methods are largely ineffective and frequently have negative impact on ranking compared to traditional SEO strategies. Even more concerning: as adoption rates increase, gains decrease, revealing a congested, zero-sum competitive dynamic.

This is the finding that should temper any breathless GEO pitch you encounter.

The Adversarial Research

Peer-reviewed papers from ETH Zurich (ICLR 2025), Harvard, and UC Berkeley (EMNLP 2024) have demonstrated that text injections can manipulate LLM rankings on production systems including Bing, Perplexity, and ChatGPT. This creates what researchers describe as a prisoner's dilemma: everyone is incentivized to game the system, but widespread adoption degrades output quality for everyone.

Stanford's citation quality research ( Nature Communications, April 2025) adds another sobering data point: 50 to 90% of LLM responses are not fully supported by their cited sources, even for GPT-4o with web search enabled.

The Pros: Why GEO Matters

Content characteristics measurably influence AI citation. This is not speculative. The Princeton study is a controlled experiment published at a top venue. Adding citations, statistics, and expert quotations to your content produces replicable gains of 22 to 115%. If you publish content that could be surfaced by AI engines, these optimizations have demonstrated, peer-reviewed impact.

AI referral traffic converts at a higher rate. While AI search platforms currently drive modest raw traffic volumes, the users who do arrive tend to have significantly higher intent. Reports from major publishers indicate 4 to 5x higher conversion rates from AI referrals versus traditional search. This makes sense: someone who gets an AI-synthesized answer with a citation has already been primed on your authority.

Passage-level optimization rewards good writing. Because AI engines extract fragments rather than whole pages, content that is self-contained, factually dense, and well-structured at the paragraph level performs best. A 50 to 150 word chunk that directly answers a question with supporting data has 2.3x higher citation rates than equivalent content buried in unstructured long-form text. This rewards clarity and precision over bloat.

Structured data amplifies visibility when done well. A controlled study of 730 pages ( Growth Marshal, February 2026) found that attribute-rich schema markup earns a 61.7% citation rate versus 41.6% for generic schema. However, generic schema actually underperforms having no schema at all. The lesson: detailed, semantically rich structured data helps. Half-hearted implementation hurts.

Multi-platform presence compounds returns. Digital Bloom's analysis of 7,000+ citations found that sites present on 4 or more platforms are 2.8x more likely to appear in ChatGPT responses. If you already have a strong multi-channel presence, AI engines are likely to amplify it further.

The Cons: Where GEO Falls Apart

The research base is extremely thin. As of early 2026, the entire academic foundation consists of roughly 2 to 3 controlled experiments. Everything else is correlational analysis or anecdotal practitioner experience. An estimated 70 to 80% of GEO advice circulating today is extrapolated from traditional SEO experience with no AI-specific validation.

Zero-sum dynamics erode advantages. The C-SEO Bench finding is critical. When everyone applies the same optimization techniques, gains disappear and can actually reverse. This mirrors the history of traditional SEO, where every innovation eventually gets competed away. Any GEO advantage you gain today is likely temporary.

Brand authority dominates on-page optimization. The strongest correlational predictor of AI citation is not any content technique but brand search volume, with a correlation coefficient of 0.334 according to Digital Bloom's data. The average domain age of ChatGPT-cited sources is 17 years. Building a 17-year-old domain is not exactly an actionable tip. The uncomfortable reality is that the factors driving most AI citation decisions (brand authority, earned media reputation, domain age) take years to build and cannot be shortcut through on-page tricks.

AI search shows systematic bias toward earned media. Research from Chen et al. found that AI search returns 81.9% earned media (third-party authoritative sources) versus Google's 45.1% in US automotive queries. Social media content, which surfaces regularly in traditional search, is virtually absent from AI results. Your brand-owned content matters less than what others say about you.

Measurement is primitive. No current GEO tool has access to real user prompts. They all rely on synthetic data, simulated searches, and statistical modeling. AI answers are probabilistic and vary by prompt phrasing, model version, and user context. There is no equivalent to Google Search Console for AI search. Only 16% of brands systematically track AI search performance according to McKinsey's 2025 CMO survey.

The GEO tools market is oversaturated relative to the problem. Over 50 dedicated GEO tools now exist, but as Jeremy Moser (CEO of uSERP) notes, 80% of GEO is good, fundamental SEO. Lorelight, a dedicated GEO platform, shut down in October 2025 after its founder concluded that GEO tracking did not actually change customer behavior. Lily Ray (VP at Amsive) warns GEO is following the same hype cycle as AMP and featured snippets.

Blocking AI crawlers is widespread and growing. An estimated 67% of publishers currently block PerplexityBot, removing themselves from Perplexity results entirely. The relationship between content publishers and AI engines remains adversarial and unresolved. Optimizing for systems that may be scraping your content without adequate compensation is a strategic question, not just a tactical one.

What GEO-Optimized Content Actually Looks Like

Based on the research, GEO-optimized content shares these characteristics:

It includes specific citations and references. Counterintuitively, citing other authoritative sources increases the probability that AI engines will cite you. LLMs interpret references as signals of rigor and reliability.

It contains quantitative data. Statistics, percentages, and specific numbers give AI engines concrete material to surface. Passages with verifiable data points are more likely to be selected during the retrieval phase.

It is structured at the passage level, not just the page level. Each paragraph or section should be self-contained and independently meaningful. AI engines extract fragments, not whole articles. A well-written, self-contained 100-word passage that answers a specific question is more valuable than a 3,000-word article where the answer is scattered across multiple sections.

It uses clear, fluent prose rather than keyword-stuffed text. Semantic matching rewards meaning and coherence. Keyword repetition degrades semantic signals and was the worst-performing strategy in the Princeton study.

It includes structured data where appropriate, but only detailed and semantically rich schema markup. Generic or minimal schema is worse than none.

It is technically accessible to AI crawlers. Server-side rendering is a hard requirement since AI bots have limited JavaScript processing. Robots.txt must permit GPTBot, PerplexityBot, and ClaudeBot if you want to be indexed.

The Bottom Line

GEO is real in the sense that AI engines select sources differently from traditional search, and measurable content characteristics influence citation probability. The Princeton study provides solid evidence that citations, statistics, quotations, and fluency produce significant visibility gains.

But GEO is not yet a mature discipline. The research base is thin, the measurement infrastructure is primitive, the most powerful factors (brand authority, earned media, domain age) are not things you can optimize in a sprint, and zero-sum dynamics mean any tactical advantage erodes as techniques become widely adopted.

The most defensible strategy is also the least novel: build genuine authority, produce original research with concrete data, earn third-party recognition, and ensure AI crawlers can access your content. The tooling and measurement will mature, but the underlying competitive advantage remains what it has always been in search. Being genuinely authoritative rather than merely optimized.

If someone is selling you GEO as a revolutionary new discipline that requires a complete rethink of your content strategy, they are probably selling you something. If they are telling you to write better, cite your sources, include real data, and make sure your content is technically accessible, they are giving you advice that has been true for as long as search engines have existed, and will remain true regardless of what architecture those engines run on.


Sources: Aggarwal et al., "GEO: Generative Engine Optimization" ( ACM SIGKDD 2024 ); Wu et al., "AutoGEO" (ICLR 2026); Puerto et al., "C-SEO Bench: Does Conversational SEO Work?" (June 2025); Digital Bloom, "2025 AI Visibility Report" ; Semrush AI Search Study (January 2026); Chen et al., "Generative Engine Optimization: How to Dominate AI Search" ; Stanford Citation Quality Research (Nature Communications, April 2025); Tramèr et al., "Adversarial Search Engine Optimization for Large Language Models" (ICLR 2025); Pfrommer et al., "Ranking Manipulation for Conversational Search Engines" (EMNLP 2024).

]]>metaend@newsletter.paragraph.com (metaend)aigeollm<![CDATA[Prompt Engineering Is Requirements Engineering in Disguise]]>https://paragraph.com/@metaend/prompt-engineering-is-requirements-engineering zEfRnkKAQhfkps98BNeQMon, 23 Feb 2026 09:02:56 GMTEvery time you write a prompt for a coding agent, you're writing a requirements spec. You just might not realize it yet.

Your prompt has the same failure modes as any requirements document: ambiguity, missing context, implicit assumptions, conflicting constraints. And just like in software engineering, the cost of getting requirements wrong compounds downstream. A vague prompt leads to a wrong first pass, which leads to a correction cycle, which leads to more tokens burned and more time wasted.

A research team from Nanyang Technological University and East China Normal University published a paper in January 2026 that formalizes this insight into a framework called REprompt. The idea is simple but powerful: run your raw prompts through the four classical stages of requirements development before the agent ever touches them.

The Four Stages

REprompt applies the same pipeline that software engineers have used for decades to turn stakeholder wishes into actionable specs:

1. Elicitation extracts what's actually in the prompt, both stated and unstated. Functional requirements (what must the output do?), non-functional requirements (quality, format, tone), implicit assumptions the user didn't bother writing down, ambiguities that could go either way, and the stakeholder intent behind the request (the why, not just the what).

2. Analysis is where the real work happens. The paper's own ablation study confirmed this: removing the Analysis stage caused the largest drop in output quality. This stage detects conflicts between requirements, ranks them by priority, resolves every ambiguity with a reasoned default, and draws clear scope boundaries. The key word here is decisive. No hedging. Pick the interpretation most likely to match what the user actually meant.

3. Specification takes the analyzed requirements and writes the optimized prompt. Every instruction unambiguous. Explicit constraints on what to include and exclude. Output format specified. Success criteria defined. Token-efficient: dense, no filler. The output of this stage is the prompt, ready to use.

4. Validation compares the optimized prompt against the original to catch drift. Did we preserve intent? Did we add scope the user never asked for? Did we lose anything important? Is it so prescriptive it kills creative latitude the user wanted? If issues are found, correct. If clean, pass through.

The Results

The team tested REprompt on MetaGPT (multi-agent software document generation) and YouWare (a vibe-coding platform with over 100,000 projects). Both LLM-as-a-judge and human evaluation showed consistent improvements.

User satisfaction on the YouWare platform hit 6.5/7 for tool-building prompts and 6.3/7 for game-building prompts. Consistency scores on system design documents reached 4.7/5. Every stage contributed: the ablation study showed that removing any one of the four stages degraded output, with Analysis being the most critical and Validation the least (though still measurably useful).

The token economics make sense too. You spend a little more upfront on the refinement pass, but you save on failed generations, clarification round-trips, and regeneration cycles. For complex tasks the ROI is immediate.

REprompt as an Agent Skill

I turned REprompt into a lightweight agent skill: a single 84-line SKILL.md file that any agent can read and follow. No dependencies, no API calls, no build step. The agent IS the LLM, so it processes the four stages using its own reasoning.

You can see the full landing page, results, and fetch the skill at:

reprompt.qstorage.quilibrium.com

Installing in OpenCode

OpenCode has a native skills system that discovers SKILL.md files automatically. One command to install globally:

mkdir -p ~/.config/opencode/skills/reprompt && \
curl -fsSL https://reprompt.qstorage.quilibrium.com/SKILL.md \
  -o ~/.config/opencode/skills/reprompt/SKILL.md

Once installed, OpenCode lists it in the agent's available skills. When you mention "reprompt" in your prompt, the agent loads the skill and runs the pipeline before proceeding.

You can also set it up as a slash command for explicit invocation. Create ~/.config/opencode/commands/reprompt.md:

---
description: Run REprompt pipeline on a prompt before execution
subtask: true
---

Read the skill at ~/.config/opencode/skills/reprompt/SKILL.md
and run its full four-stage pipeline on the following prompt:

$ARGUMENTS

Then just type /reprompt make me a todo app and the agent will run your prompt through all four RE stages, show you the optimized version, and ask whether to proceed, adjust, or show the full trace.

The subtask: true flag keeps the RE processing in a child session so it doesn't pollute your main working context.

When to Use It

REprompt isn't needed for every prompt. If you're asking the agent to fix a typo or run a test, skip it. But for anything where you'd normally expect to iterate, where the prompt is vague, the task is complex, or the domain has implicit conventions the agent might miss, running the pipeline first will save you time and tokens.

The quick mode (documented in the skill) combines stages for simple prompts under 30 words, so even the overhead is adaptive.

The deeper takeaway from the paper is worth sitting with: if you're spending real money on AI coding tools, the discipline of treating your prompts as requirements specs, not casual requests, might be the highest-leverage optimization available to you. Not a new model, not a new framework. Just being precise about what you want.


REprompt is based on "REprompt: Prompt Generation for Intelligent Software Development Guided by Requirements Engineering" by Shi et al. (2026), arXiv:2601.16507 .

Skill and landing page by meta -- get in touch .

]]>metaend@newsletter.paragraph.com (metaend)airepromptopencodeagents<![CDATA[The End of Screen Scraping: Why Your Website Needs a "Native Language" for AI Agents (And How to Get It Before Your Competitors)]]>https://paragraph.com/@metaend/webmcp-audit-implementation-guide Bbapx9TD4VhrTn3ITatNTue, 17 Feb 2026 12:19:59 GMTFor the last two years, we've watched AI agents try to "browse" the web like a clumsy human wearing blinders.

They take screenshots. They feed them into massive vision models. They guess where the "Submit" button is based on pixel coordinates. And if your web designer moves that button five pixels to the left? The agent breaks. The transaction fails. The user gets frustrated.

It's slow, it's expensive, and frankly, it's a fragile way to build the future of the internet.

But as of this week, the rules have changed.

Google AI has just introduced the Web Model Context Protocol (WebMCP), a groundbreaking shift that turns your website from a static image into a structured toolkit that AI agents can understand natively.

If you are building AI agents, running an e-commerce platform, or managing a SaaS product, this is the most important infrastructure update since HTTPS. Here is why you need to care, and how you can implement it today.

The Problem: AI Trying to "Read" Pixels

Currently, when an AI agent interacts with your site, it's essentially playing a high-stakes game of "Where's Waldo?" using computer vision.

The Solution: WebMCP

WebMCP flips the script. Instead of the AI guessing how to use your site, your site tells the AI exactly what it can do.

Think of it as giving your website a voice. Through WebMCP, your HTML and JavaScript expose capabilities directly to the browser's AI layer. The AI no longer sees a picture of a form; it sees a structured JSON schema defining inputs, descriptions, and actions.

The Numbers Don't Lie

According to early data from Google's announcement, the shift from vision-based browsing to WebMCP offers:

How It Works: Two Paths to Integration

Google has made this accessible for everyone, from simple blogs to complex enterprise apps.

1. The Declarative Approach (For Simple Forms)

This is the low-hanging fruit. If you have standard forms (Contact Us, Newsletter, Search), you can make them AI-ready by simply adding attributes to your existing HTML:

<form toolname="book_flight" tooldescription="Books a flight based on destination and date">
  <!-- inputs -->
</form>

Chrome automatically reads these tags and creates a tool schema for any connected AI agent. When the AI fills the form, your backend receives a SubmitEvent.agentInvoked, letting you know a machine—not a human—is driving the action.

2. The Imperative Approach (For Complex Apps)

For dynamic Single Page Applications (SPAs) like shopping carts or dashboards, you can use the new JavaScript API:

navigator.modelContext.registerTool({
  name: "add_to_cart",
  description: "Adds an item to the user's current session cart",
  schema: { ... }
});

This allows for multi-step workflows that happen in real-time within the user's session, without needing to re-login or bypass security headers.

Security: The "Permission-First" Promise

A common question from CTOs is: "Do I want AI robots clicking buttons on my site?"

WebMCP is designed as a permission-first protocol. The browser acts as a secure mediator. Before an agent executes a sensitive action (like booking a flight or transferring funds), Chrome can prompt the user: "Allow AI to book this flight?"

This keeps the human in the loop while allowing the agent to do the heavy lifting. Plus, with methods like clearContext(), you can ensure session data is wiped immediately after the task, preserving privacy.

The First-Mover Advantage: The Early Preview Program (EPP)

Here is the critical part: Google is not waiting for a general release to let you start.

They have launched the Early Preview Program (EPP) for Chrome 146. This is a limited window where developers can test these features now.

By the time WebMCP is default in every browser, the companies that have already optimized their schemas will dominate the "Agentic Web" search results.

Is Your Website Ready for the Agentic Era?

The transition from "screen scraping" to "structured interaction" is not just an upgrade; it's a survival requirement for the next generation of AI traffic.

However, implementing this correctly requires more than just copying code snippets. You need to:

  1. Audit your high-value workflows to identify which actions should be exposed as tools.

  2. Architect the JSON schemas to prevent LLM hallucinations.

  3. Secure your endpoints with the new permission gates.

  4. Enroll in the EPP to get ahead of the curve.

🚀 Let's Future-Proof Your Stack

Don't let your website be the one that breaks when the AI revolution fully hits.

I am currently opening slots for a "WebMCP Readiness Audit & Implementation" sprint.

In this engagement, we will:

👉 [Click Here to Book Your WebMCP Audit Call]

The Agentic Web is here. Make sure your website speaks its language.

]]>metaend@newsletter.paragraph.com (metaend)webmcpgoogleaiagentic