An interview you can give while you sleep

4 September 2026

There is a terminal on this site now. You type a question about me into it and an AI answers in the first person, grounded in a small knowledge base I wrote — résumé, selected work, the engineering records behind years of project work, a page about life outside the terminal — and every answer ends with footnotes pointing at the document it came from.

The model was the easy part. It is a retrieval pipeline on AuraMind, the agent platform we build at Arc10, and wiring a streaming completions endpoint to a text box is a solved problem. Nearly everything that took real thought was about the promises the page makes — “ask me anything”, “every answer cites its source”, “this speaks for me” — and what it costs to keep them when the thing runs unattended, in public, with my name on it.

The vow you break on purpose

This site has a rule: no client JavaScript except the theme toggle. The rule exists because every feature that needs a script is a feature that breaks for someone, and a personal site should be the most durable page a visitor meets all day.

An interactive terminal cannot keep that vow, so the decision was to break it deliberately and contain the break. The page you get without JavaScript is not an apology — it is a full transcript of a real exchange, footnotes included, with an email address at the bottom for the human version. The widget boots by replacing the static transcript’s cursor line with a live input, so the page never renders empty waiting for a script, and a failed script leaves a page that still says everything it needs to. Progressive enhancement is an old idea. It is also still the only honest answer to “what happens when your JavaScript doesn’t load?”

A citation is a promise

The line under the hero says every answer cites the source it came from. That sentence turned out to be the most expensive one on the page.

It gated the launch once for a plumbing reason: the platform routed the question to a specific agent as a delegated sub-run, and the retrieval citations attached to the sub-run while the response’s sources came from the parent — grounded answers, empty footnotes. The page stayed unlinked until that was fixed upstream, because shipping the sentence without the footnotes would have made it copy instead of a contract.

It also gated the launch once for a reason I found more instructive. Asked about Praetor, the terminal confidently answered that it “wasn’t shut down or acquired” — while the site one click away says Akash Network acquired it. The engineering record in the knowledge base described the open-sourcing and the handover but never actually wrote the sentence “the product was acquired”. The model didn’t hallucinate; it read what I wrote, and I hadn’t written it. Retrieval answers from what you wrote down, not from what you meant, and the fix belongs in the document, not the prompt. I have come to like this failure mode: it is auditable. When the bot is wrong, there is a file to blame, and a diff that fixes it.

The budget is the security model

A common first question about a public AI endpoint is how it resists prompt injection. My answer is that mostly, it doesn’t have to — because of a decision about the data rather than the prompt. Everything in the knowledge base is public by definition. It is the same material as the site itself. There is no secret for a jailbreak to extract, so a successful jailbreak wins a strangely worded version of my résumé.

What the endpoint does have to resist is cost. An unattended text box wired to a paid model is a hose someone else can point at my bill. So the browser never sees the platform or a key; it talks to a small same-origin relay — a zero-dependency Node service — that holds the credentials and enforces two limits: a per-IP rate limit (twenty questions an hour, keyed to the one hop of X-Forwarded-For our own proxy appended, because every hop before it is client-supplied fiction) and a daily budget breaker for the endpoint as a whole. When the breaker trips, the terminal says so in character and hands out my email address. The worst day an attacker can give me costs a known number of dollars and ends with the same fallback the no-JavaScript visitor gets.

Streams fail in two places

Answers arrive over server-sent events, and a stream has two distinct ways to fail: before it starts, and after. The relay keeps them separate on purpose. Anything that dies pre-stream — a malformed question, the rate limit, the spent budget — is an ordinary JSON error with a code, and the widget renders designed copy for each code rather than a stack trace. Anything that dies mid-stream arrives as an event inside the stream, after real text may already be on screen.

That second case is where the client has to match the server’s honesty. The easy implementation catches the broken read and cleans up everything, including the half answer the visitor was reading — which converts a network hiccup into the interface snatching text away mid-sentence. The whole fix is one guard on the cleanup:

} catch {
  if (answer.childNodes.length === 0) answer.remove();
  say(errorLine('upstream_down'), true);
}

A pre-stream failure leaves nothing worth keeping; a connection that drops mid-answer must not erase what already streamed. Everything else stays.

The terminal keeps whatever streamed and appends the failure line under it, the same shape the server’s own mid-stream error takes. A partial answer with an honest “the AI is down” under it is strictly more useful than a clean slate.

The rendering side has one rule with no exceptions: model output never becomes markup. Every line the stream produces is built out of text nodes — a small normalizer turns the model’s stray markdown into terminal styling, and anything that would need innerHTML simply doesn’t get rendered. A machine whose words I don’t control writing into a page I sign is exactly the place for the boring, absolute version of the rule.

What it does not solve

The terminal is a rehearsed version of me. It answers from what I chose to write down, which means it is precisely as good as the knowledge base is honest and no better — the in-character copy can make an error sound charming, but it cannot make a thin document thick. It will decline things a conversation would surface, and it has never once changed its mind, which is the most unrealistic thing about it.

And there is a loop in it I decided to enjoy rather than resist: the build that publishes this site also syncs the knowledge base, and posts on this page are part of what gets ingested. Which means that by the time you read this, the terminal can answer questions about how it was built — citing, as its source, the post you just read.

← All writing