Code is the source of truth; these claims no longer matched runtime/src: - "net.connect does not exist" — landed 2026-09-07 (id 110); net.connect_tls / read_tls / write_tls (115-117) + net.accept_tls (118), WO_B_MAX 118. Fixed in jarvis 00-story (problem statement + architecture + out-of-scope), porch 00-story (proxy middleware row), rv2 7 (push-collector fork), 00-code-review - "TLS: none / proxy-mandated forever" — retired by rv2 9 (in-process TLS both directions). Fixed in porch + web-app + site example READMEs (proxy is now a deployment choice; HSTS row), 00-code-review - "no RNG anywhere in the runtime" — imprecise: the runtime has a getrandom(2) source since rv2 9 (TLS ephemerals), but nothing exposes it to .wo yet. Fixed in CODE-LOGIC (digests), lang 34, porch 2, status lang-39 row - "porch 9 blocked on language 41" — lang 41 fixed 63065ff. Fixed in porch 1, jarvis 00-story, status NEXT PLAN, dependency graph (L41 done, P9 ready) - dependency graph §7 rewritten: the runtime side is done; jarvis 1 waits only on porch (developer's porch-first order). Adds jarvis 1's dependency table + the build order that satisfies it - 00-code-review: a dated 2026-09-09 re-verification appended (record kept) - site README lives in the writeonce-site submodule: committed there, pointer bumped here Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit f1049dd9b7c7770da28bcabfc1cb1324621e7ee6)
7.3 KiB
Story — jarvis, the writeonce AI assistant
The sixth track, and the second whose product is an end-user program rather
than a language capability — after wmux, the terminal
multiplexer. Where wmux proves writeonce can build the tool a developer lives
in, jarvis proves it can build the tool of the moment: an AI assistant, written
end to end in .wo, durable and single-binary by construction. It serves the
same north star — adoption by Linux developers — by meeting them where the
attention is.
Numbering restarts at 1 and is local to this track; frontmatter carries
track: jarvis. Status rules are the repo's, unchanged: status: in
frontmatter is the only place state lives, no directory encodes it.
The problem, stated once
An assistant's whole job is to reach a model. When this track was written
(2026-09-07) the runtime could not reach anything outbound — it had learned to
listen (sockets in 8/11/35, unix sockets and peer address in 35 and runtime-v2)
but never to dial: no net.connect, no outbound TLS. Both gaps are now
closed (confirmed against runtime/src/wob.h): net.connect (id 110, TCP) and
the full TLS 1.3 client net.connect_tls/net.read_tls/net.write_tls (ids
115–117, runtime-v2 9, live-gated) — plus inbound net.accept_tls (118) for porch.
An LLM API is HTTPS on a remote host, and the runtime can now dial it directly.
The design chosen is direct outbound HTTPS — jarvis dials the LLM API itself, keeping the pure single-binary story. That gates the whole track on runtime work, now partly built:
net.connect— outbound TCP — ✅ landed 2026-09-07 (wob.hid 110,getaddrinfoDNS + blocking connect; the outbound half language 38 named).- An outbound TLS client — HTTPS over that socket — owned by
runtime-v2 9 (in-process TLS), which
retires the standing "TLS is the proxy's job" doctrine. In progress and
mostly landed: A AEAD (ChaCha20-Poly1305 + AES-GCM), B HKDF,
C X25519, D signatures (RSA PKCS1/PSS + ECDSA-P256), E X.509 +
SAN/hostname, F1 record layer, F2 key schedule, F3a message
layer, F3b offline handshake verification, and the F3c-core sans-io
handshake driver — all vector-gated against RFC 8448 and real cert chains.
What jarvis still waits on is F3c-net: the
net.connect_tlsbuiltin (the socket glue driving that driver over a real fd) plus a system CA trust-anchor walk — and G (inbound server) for porch, not jarvis.
A local-gateway alternative was considered and set aside: jarvis could speak
to a small companion process over a unix socket (net.connect_unix, id 107) or
spawn one (proc.spawn, id 97) — both exist today — and let that companion do
the HTTPS, exactly as inbound TLS terminates at a proxy. It is buildable now.
It was rejected in favour of the single-binary story, in which the assistant
owns its own connection rather than shipping a second executable.
Architecture
browser ⇄ jarvis (a porch app) ⇄ net.connect_tls ✅ (rv2 9, done) ⇄ LLM API
Requests arrive at a porch web app; the answer streams the other way, token by
token, LLM → jarvis → browser, over porch's SSE. Conversation state is durable
in a @table, so history survives a restart with no external store — the
writeonce differentiator wmux already showed for session state, applied to chat.
The iterations
Ordered by dependency; the first rung is the whole end-to-end seam, and nothing past it is worth building until that seam is proven.
| # | Iteration | Delivers | Needs |
|---|---|---|---|
| 1 | the chat loop — ✅ ready (forks auto-approved, review_pending) |
a prompt sent to one LLM, tokens streamed back to the browser, the conversation persisted durably | the outbound seam (TLS phase F); porch 2/3/6/7; wo-html |
| 2 | tool use / the agent loop — refine |
function-calling and multi-step orchestration through actors — where "assistant" becomes "agent" | 1 |
| 3 | retrieval (RAG) — refine |
embeddings + vector search over a document set; carries its own sub-gap — an embeddings call over the same outbound path, plus a vector store (pure-.wo or a new primitive, decided in that story) |
1, and the embeddings/vector decision |
Sketched, not committed — named so the shape is visible, not to schedule them: model routing / multi-model (choose a backend per request) and an MCP client (jarvis as an MCP host, calling tools over the protocol) — both on-brand, both later.
Only iteration 1's scope is settled by this overview; every iteration file is
written and refined to ready before its code lands, per the repo's story
discipline.
Sequencing (set 2026-09-09): the outbound TLS seam is done (runtime-v2 9), so jarvis is no longer blocked on the runtime. The developer set the build order: jarvis is implemented once porch is complete and ready for all future jarvis iterations — jarvis's chat loop is a porch app, so porch lands first, then jarvis 1–3.
Dependencies
Consumed, and already ready or shipped:
| Needs | From |
|---|---|
| signed cookies, session id | porch 2 |
| durable conversation history, revocable sessions | porch 3 + @table |
| incremental response writes | porch 6 |
| token streaming to the browser | porch 7 (SSE) |
| the chat UI | wo-html / writeonce-view |
Blockers, which must land before iteration 1 starts:
| Blocker | Owner | State |
|---|---|---|
outbound TCP (net.connect) |
language 38 | ✅ landed 2026-09-07 (wob.h id 110) |
| outbound TLS client | runtime-v2 9 — in-process TLS; retires the proxy-termination doctrine | ✅ LANDED 2026-09-09 — the full client: A–E crypto, F1–F3c handshake, SAN/hostname + basicConstraints/EKU chain validation, and net.connect_tls / net.read_tls / net.write_tls (ids 115–117), live-gated (just tls, 5/0) from .wo incl. untrusted-chain + hostname-mismatch negatives. jarvis's outbound seam is open (G inbound server is porch's, not jarvis's) |
What this track does NOT own
| Not jarvis's | Why |
|---|---|
| local, in-process model inference | needs an ML runtime and heavy FFI — against the no-external-dependency doctrine |
| the local-gateway companion process | considered and rejected (above) in favour of the single-binary story |
| voice / audio in or out | a separate surface with its own capture and codec story; no rung asks for it |
| starting before porch is complete | the runtime blockers (net.connect, TLS, language 41) have all landed; what jarvis now waits on is the framework — the developer set jarvis to follow porch completion (see Sequencing) — documented, not worked around |
Review protocol
Same as every track: the developer reads one iteration, approves or amends, and the next starts only after approval. Each iteration is an unsplittable value slice with phases, Given/When/Then acceptance criteria, and an out-of-scope list, proven by a gate before it is called done.