# 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`](../wmux/00-story.md), 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.h` id 110, `getaddrinfo` DNS + blocking connect; the outbound half language 38 named). - **An outbound TLS client** — HTTPS over that socket — owned by runtime-v2 [9](../runtime-v2/09-in-process-tls.md) (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_tls` builtin (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](01-chat-loop.md) — ✅ `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](02-tool-use.md) — `refine` | function-calling and multi-step orchestration through actors — where "assistant" becomes "agent" | 1 | | 3 | [retrieval (RAG)](03-retrieval.md) — `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](../porch/00-story.md) 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](../porch/02-randomness-and-cookies.md) | | durable conversation history, revocable sessions | porch [3](../porch/03-sessions.md) + `@table` | | incremental response writes | porch [6](../porch/06-streaming-core.md) | | token streaming to the browser | porch [7](../porch/07-sse-and-compression.md) (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](../language-runtime-database/38-content-platform-capabilities.md) | ✅ **landed 2026-09-07** (`wob.h` id 110) | | outbound TLS client | runtime-v2 [9](../runtime-v2/09-in-process-tls.md) — 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.