From 8992dbd5895479b6e1ad7d768cbcc2db2341adf9 Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Wed, 9 Sep 2026 05:03:30 +0200 Subject: [PATCH] docs(rv2-tls): rv2 9 COMPLETE (both directions); retire proxy doctrine; jarvis-after-porch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rv2 9 story -> status: done. §G G3 landed; ladder A–G complete, live-gated both directions (just tls 5/0, just tls-server 4/0). review_pending + phase rows + G sub-phases updated - doctrine retired where the story named it: language 34 ("TLS permanently the proxy's job"), language 38 ("proxy-terminated ... no HTTPS clients"), porch 00-story ("TLS ... proxy-terminated") — each corrected to point at in-process TLS (net.connect_tls / net.accept_tls) - status board: rv2 9 row DONE + a top summary; NEXT PLAN = porch then jarvis (sequencing set: jarvis follows porch) - jarvis 00-story: sequencing note (no longer runtime-blocked; porch first) - CODE-LOGIC: the inbound-server section (net.accept_tls, signing, slot refactor, RST-drain, gate) Co-Authored-By: Claude Opus 4.8 (cherry picked from commit f3a3c962e5f288c25e505851edef4e0a5df9a85f) --- docs/stories/00-status.md | 39 +++++++++++++++---- docs/stories/jarvis/00-story.md | 6 +++ .../34-crypto-builtins.md | 6 ++- .../38-content-platform-capabilities.md | 9 +++-- docs/stories/porch/00-story.md | 3 +- docs/stories/runtime-v2/09-in-process-tls.md | 19 +++++---- runtime/src/CODE-LOGIC.md | 16 +++++++- 7 files changed, 76 insertions(+), 22 deletions(-) diff --git a/docs/stories/00-status.md b/docs/stories/00-status.md index 7d40d05..b184a00 100644 --- a/docs/stories/00-status.md +++ b/docs/stories/00-status.md @@ -76,7 +76,23 @@ behind this board; live Obsidian Dataview views: ## ▶ NEXT PLAN -### Landed 2026-09-09 — the outbound TLS 1.3 client is COMPLETE (rv2 9 A–F3c), live-gated; jarvis unblocked +### Landed 2026-09-09 — rv2 9 in-process TLS 1.3 is COMPLETE, both directions, live-gated + +**rv2 9 DONE.** In-process TLS 1.3 both directions, hand-rolled, RFC-8448/real-cert +gated, retiring the proxy-termination doctrine (34/38/porch corrected). **Outbound** +`net.connect_tls`/`read_tls`/`write_tls` (ids 115–117) — `just tls` 5/0. +**Inbound** `net.accept_tls` (id 118) — `just tls-server` 4/0 (openssl s_client, +EC + RSA). Signing is the runtime's first private-key crypto: constant-time +RSA-PSS + ECDSA-P256 with an RFC 6979 nonce, plus `wo_pkey_parse` +(PKCS#8/PKCS#1/SEC1). `test_tls` 123/0, `test_crypto` 130/0, full suite 0 fail. +Deferred (named follow-ups, none blocking): park-based handshake, `TlsConn` +language object, connection pooling, close_notify on shutdown, complete-formula +EC ladder. **Next: porch** (then jarvis — the developer set jarvis to follow +porch completion). Separately open: language 41's marshal fix (unblocks porch 9). + +
outbound client detail (F-phases) + +### Landed 2026-09-09 — the outbound TLS 1.3 client (rv2 9 A–F3c), live-gated; jarvis unblocked **What happened (code + docs):** a whole hand-rolled TLS 1.3 client, in `runtime/src/crypto.c` + new `tls.c`/`tls.h` + the `net.*_tls` builtins, gated @@ -100,12 +116,19 @@ trap `WO_T_IO`; per-shard lazy read-only CA bundle `WO_CA_BUNDLE`; handshake deadline `WO_TLS_HANDSHAKE_MS`; basicConstraints+EKU hardening). Forks auto-approved 2026-09-08/09, `review_pending` for a developer second review. -**Next step — jarvis 1 (the chat loop) is now buildable** (its outbound seam is -open); or rv2 9 **G** (inbound TLS server) for porch, which also lets the -proxy-termination doctrine docs (34/38/porch) be corrected. Deferred rv2 9 -follow-ups: a park-based handshake, a first-class `TlsConn` object, connection -pooling. (Separately still open: language 41's marshal fix — below — unblocking -porch 9.) +(This F-phase detail is superseded by the rv2-9-DONE summary above.) + +
+ +### Sequencing set 2026-09-09 — jarvis follows porch + +The developer set the order: **jarvis is implemented once porch is complete and +ready for all future jarvis iterations**. So with rv2 9 (the outbound seam) done, +the path is **porch first** — the framework the jarvis chat loop is written on — +then jarvis 1–3. porch's own blocker is language 41's marshal fix (unblocks +[porch 9](porch/09-idempotent-replay.md)); the porch track (2–8) is brainstormed +to `ready`, its language bill three small builtins (`random_bytes`, +`deflate`/`crc32`, `time.utc`). ### Brainstormed 2026-09-06 — the porch track (2–8) and language 41's fix, both to `ready` @@ -1574,7 +1597,7 @@ starts. Edges in [dependency graph section 6](../00-dependency-graph.md). | 6 | [term.size + term.width](runtime-v2/06-term-size-width.md) | ✅ **DONE 2026-09-02** — TIOCGWINSZ read twin (nil = not a tty) and libc wcwidth under C.UTF-8; the only runtime work the whole wmux parity ladder needs | | 7 | [observability](runtime-v2/07-observability.md) | ⬜ `refine` — **moved here 2026-09-06** from language iteration 30 (`was_language_iteration: 30`). Runtime metrics/gauges, a `pprof`-equivalent profile, stack-trace-on-trap; consumers named (porch [8](porch/08-static-and-lifecycle.md)/[39](language-runtime-database/39-web-framework-parity.md), databasev2 [5](databasev2/05-bounded-tables-eviction.md), the limiter's lazy expiry). Forks: counters-only vs profiling, exposition format, pull vs push, trace-on-trap as a separable first slice. Stretches the track's charter (instrumentation, not processes/terminals/signals) — noted in the story | | 8 | [symmetric cipher (AEAD)](runtime-v2/08-symmetric-cipher.md) | 🔄 **in-progress** — the **first rung of the TLS ladder** (gates rv2 9). **Phases A + B + C LANDED 2026-09-08**: A ChaCha20-Poly1305 (ids 111/112, RFC 8439 §2.8.2); B AES-128/256-GCM (ids 113/114) via AES-NI+PCLMULQDQ; C portable constant-time software AES-GCM fallback (S-box via GF-inverse ladder, bit-by-bit GHASH) — AES-GCM now on any CPU, dispatched hw-or-sw. All hand-rolled, constant-time, both AES paths NIST cases 4 & 16 byte-exact, KAT-gated in test_crypto (**48/0**), ASan/UBSan clean. Remaining: D cookie wrapper → E gate (ARMv8 hw path deferred). Consumers: rv2 9 TLS + porch encrypted cookies | -| 9 | [in-process TLS](runtime-v2/09-in-process-tls.md) | 🔄 **in-progress** — **outbound client COMPLETE 2026-09-09**, **retiring the "TLS is the proxy's job" doctrine** (34/38/porch). Locked: **hand-roll TLS 1.3**, **1.3-only**, **RSA+ECDSA+full X.509**. Ladder (KAT'd vs **RFC 8448** / real certs, ASan/UBSan clean): **A AEAD ✅ → B HKDF ✅ → C X25519 ✅ → D signatures ✅ → E X.509 + SAN/hostname + basicConstraints/EKU ✅ → F1 record ✅ → F2 key schedule ✅ → F3a messages ✅ → F3b offline verify ✅ → F3c-core sans-io driver ✅ → F3c-net `net.connect_tls`/`read_tls`/`write_tls` ✅** (ids 115–117; deadline-bounded blocking handshake then parked data plane; per-shard fd-keyed no-lock slots; CA bundle via `WO_CA_BUNDLE`). **Live-gated** `just tls` (5/0) from `.wo` incl. untrusted-chain + hostname-mismatch negatives. `tls.c`/`tls.h`; test_tls 107/0, test_crypto 104/0, full suite 0 fail. **Remaining: G inbound server** (porch) + deferred park-handshake/`TlsConn`/pooling. Forks auto-approved 2026-09-08/09, `review_pending`. The project's **highest-risk** work; mandatory reference-tested/constant-time/negative-test gates | +| 9 | [in-process TLS](runtime-v2/09-in-process-tls.md) | ✅ **DONE 2026-09-09** — in-process TLS 1.3 **both directions**, **retired the "TLS is the proxy's job" doctrine** (34/38/porch corrected). Hand-rolled, 1.3-only, RSA+ECDSA+full X.509; KAT'd vs **RFC 8448** / real certs, ASan/UBSan clean. **A–E crypto** (AEAD, HKDF, X25519, sign/verify, X.509 + SAN + basicConstraints/EKU) → **F client** (`net.connect_tls`/`read_tls`/`write_tls`, ids 115–117) → **G server** (constant-time RSA-PSS + ECDSA-P256 signing w/ RFC 6979, server FSM, `net.accept_tls` id 118, `wo_pkey_parse`). Live-gated: `just tls` 5/0 (outbound) + `just tls-server` 4/0 (inbound, openssl s_client EC+RSA). test_tls 123/0, test_crypto 130/0, full suite 0 fail. Deferred follow-ups (non-blocking): park-based handshake, `TlsConn` object, connection pooling, close_notify, complete-formula EC ladder. Forks auto-approved 2026-09-08/09, `review_pending`. The project's **highest-risk** work — done | ### ▸ wmux — the terminal multiplexer track diff --git a/docs/stories/jarvis/00-story.md b/docs/stories/jarvis/00-story.md index f09fc4c..1677ed9 100644 --- a/docs/stories/jarvis/00-story.md +++ b/docs/stories/jarvis/00-story.md @@ -76,6 +76,12 @@ 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: diff --git a/docs/stories/language-runtime-database/34-crypto-builtins.md b/docs/stories/language-runtime-database/34-crypto-builtins.md index aee2882..4983394 100644 --- a/docs/stories/language-runtime-database/34-crypto-builtins.md +++ b/docs/stories/language-runtime-database/34-crypto-builtins.md @@ -80,7 +80,11 @@ only the digests are missing. - Asymmetric crypto (ed25519 signatures/keypairs) — held iteration 21's spec decides what it needs when it unholds; this iteration lays the digest floor it will stand on. -- TLS — permanently the proxy's job (framework doctrine). +- TLS — ~~permanently the proxy's job (framework doctrine)~~ **RETIRED + 2026-09-09.** The runtime now speaks TLS 1.3 in-process, both directions + (hand-rolled, RFC-8448-gated) — see [runtime-v2 9](../runtime-v2/09-in-process-tls.md). + This iteration's digests are a rung of that ladder, not a floor beneath a + proxy boundary. - CRC32 — the ledger lists it, but no consumer is blocked on it; it joins only if 24's spec finds a real need (rejecting speculative surface). diff --git a/docs/stories/language-runtime-database/38-content-platform-capabilities.md b/docs/stories/language-runtime-database/38-content-platform-capabilities.md index 077ccdb..2a45ae3 100644 --- a/docs/stories/language-runtime-database/38-content-platform-capabilities.md +++ b/docs/stories/language-runtime-database/38-content-platform-capabilities.md @@ -111,9 +111,12 @@ readiness: refine - **Full-text search.** The engine indexes equality probes on declared columns; there is no prefix scan or FTS. Query-grammar growth is [databasev2 8](../databasev2/08-query-grammar-corpus.md)'s. -- **TLS** — proxy-terminated, by doctrine, unchanged. The outbound half - therefore speaks plaintext to a local sidecar or a trusted-network peer, - and the story says so out loud rather than implying HTTPS clients. +- **TLS** — ~~proxy-terminated, by doctrine~~ **RETIRED 2026-09-09.** The + runtime now speaks TLS 1.3 in-process both directions — this iteration's + `net.connect` gained an HTTPS sibling `net.connect_tls`, and porch can + terminate inbound TLS with `net.accept_tls`, all in + [runtime-v2 9](../runtime-v2/09-in-process-tls.md). The plaintext-to-a-sidecar + framing above no longer holds. - **A plugin/app ecosystem.** In-runtime recompile is [iteration 26](26-blue-green-deploy.md)'s; nothing here loads code at run time. diff --git a/docs/stories/porch/00-story.md b/docs/stories/porch/00-story.md index 4daa21d..a9dc0eb 100644 --- a/docs/stories/porch/00-story.md +++ b/docs/stories/porch/00-story.md @@ -74,7 +74,8 @@ risky work starts. | TTL cache, `transaction { }`, durable job queue | language: [iteration 18](../language-runtime-database/18-memory-db-features.md) | | a `proxy` middleware | language: [iteration 38](../language-runtime-database/38-content-platform-capabilities.md) — needs `net.connect`, which does not exist | | metrics, profiling, per-change CI, fuzzing | [runtime-v2 7](../runtime-v2/07-observability.md) — observability (was language iteration 30; metrics/profiling/trace-on-trap; CI + fuzz are tooling, split out) | -| TLS, HTTP/2 | nobody — proxy-terminated by doctrine | +| TLS | ✅ [runtime-v2 9](../runtime-v2/09-in-process-tls.md) — in-process TLS 1.3 both directions (2026-09-09); porch can terminate inbound TLS with `net.accept_tls`, no front proxy required. The proxy-termination doctrine is retired | +| HTTP/2 | nobody yet — a separate protocol slice; TLS is its prerequisite, now met | | a runtime template engine | nobody — rejected; markup is a compile-time literal (`writeonce-view`) | | a radix-tree router | nobody yet — waiting on a *measurement*, not a decision | diff --git a/docs/stories/runtime-v2/09-in-process-tls.md b/docs/stories/runtime-v2/09-in-process-tls.md index 1e4dab9..ec4fdf1 100644 --- a/docs/stories/runtime-v2/09-in-process-tls.md +++ b/docs/stories/runtime-v2/09-in-process-tls.md @@ -1,9 +1,9 @@ --- track: runtime-v2 iteration: "9" -status: in-progress +status: done readiness: ready -review_pending: "forks auto-approved 2026-09-08/09 for autonomous execution — developer second review before this ships. OUTBOUND CLIENT COMPLETE + live-gated (just tls, 5/0): A–E crypto, F1 record, F2 key schedule, F3a messages, F3b offline verify, F3c-core sans-io driver, SAN/hostname, F3c-net chain validation + basicConstraints/EKU, and the net.connect_tls/read_tls/write_tls builtins (ids 115-117). Six integration forks implemented as locked. REMAINING: G inbound server (porch); deferred park-based handshake + TlsConn object + connection pooling; correcting the doctrine docs (34/38/porch)" +review_pending: "forks auto-approved 2026-09-08/09 for autonomous execution — developer second review before this ships. COMPLETE BOTH DIRECTIONS, live-gated (just tls 5/0 outbound, just tls-server 4/0 inbound EC+RSA). Outbound: A–E crypto, F1–F3c client (net.connect_tls/read_tls/write_tls, ids 115-117). Inbound: G1 constant-time RSA-PSS + ECDSA-P256 signing (RFC 6979), G2 server FSM, G3 net.accept_tls (id 118) + private-key parse. Deferred (named follow-ups, not blockers): park-based handshake, TlsConn language object, connection pooling, TLS close_notify on shutdown, complete-formula EC ladder. Doctrine docs (34/38/porch) corrected as part of this landing" --- # runtime-v2 9 — in-process TLS: retiring the proxy-termination doctrine @@ -80,7 +80,7 @@ they may split into their own runtime-v2 iterations as they are picked up. | D — signatures | ✅ **LANDED 2026-09-08** — **RSA** `wo_rsa_pkcs1_sha256_verify` + `wo_rsa_pss_sha256_verify` (bignum Montgomery modexp) and **ECDSA-P256** `wo_ecdsa_p256_sha256_verify` (Jacobian point arithmetic, a=-3, on-curve check, Fermat inverses reusing the bignum). Verification is public data so **not** constant-time by design. Both match python vectors (RSA-2048 PKCS1+PSS; P-256), tamper/wrong-hash rejected, KAT-gated, ASan/UBSan clean | | E — X.509 | 🔄 **CORE LANDED 2026-09-08** — a defensive ASN.1/DER reader (every length/bound checked, malformation is rejection not over-read) + certificate parse (tbsCertificate span, sig-alg OID, signature, SubjectPublicKeyInfo→RSA n/e or EC P-256 x/y, validity) + `wo_x509_verify_one` (one chain link's signature, dispatching to D's RSA-PKCS1/PSS + ECDSA-P256) + `wo_x509_parse_spki` + `wo_x509_check_validity` (caller supplies the time). KAT-gated in `test_crypto.c` against **real python-generated chains** — RSA CA+leaf (SHA256withRSA) and EC P-256 CA+leaf (ecdsa-with-SHA256): leaf-vs-CA, self-signed CA, wrong-issuer/tampered/truncated rejected, validity window, SPKI extraction — ASan/UBSan clean. **Deferred to F**: SAN/hostname match (needs the target host) and the multi-cert chain walk to a system CA bundle | notoriously bug-prone; consumes D | | F — record + handshake (client) | ✅ **COMPLETE 2026-09-08/09** (client). F1–F3b LANDED 2026-09-08 — new `tls.c`/`tls.h`. **F1 record layer** (`wo_tls_record_seal`/`open`, RFC 8446 §5.2, per-record nonce = iv XOR seq, both suites) KAT'd byte-for-byte vs python. **F2 key schedule** (`wo_tls_derive_handshake`/`_application`/`_traffic_keys`/`_finished_verify`, §7.1) KAT'd byte-for-byte vs **RFC 8448 §3**. **F3a message layer** (`wo_tls_parse_server_hello` — attacker input, bounded, rejects HRR/bad suite/truncation; `wo_tls_build_client_hello` — SNI, x25519, sig-algs) KAT'd vs RFC 8448 SH + validated by an independent parser. **F3b offline handshake verification** (`wo_tls_verify_cert_verify` over phase E+D; server + client Finished) — the whole handshake **crypto** proven end-to-end offline vs RFC 8448. **F3c-core sans-io driver** (`wo_tls_client` — pure FSM, caller frames records: CH→SH→flight→Finished, message reassembly, per-message transcript timing, constant-time Finished, application encrypt/decrypt) KAT'd against the **full RFC 8448 record trace** — client Finished + first app record byte-for-byte, NewSessionTicket + server app data decrypt, tampered flight refused. **SAN/hostname** (`wo_x509_check_host`, RFC 6125) + driver enforcement landed. **F3c-net chain validation** (`wo_tls_verify_chain`) + **basicConstraints/EKU** hardening KAT'd offline. **F3c-net socket/VM ✅ LANDED 2026-09-09**: `getrandom` ephemeral, per-shard lazy CA-bundle loader (`WO_CA_BUNDLE`), and the `net.connect_tls` / `net.read_tls` / `net.write_tls` builtins (ids 115–117; blocking deadline-bounded connect+handshake then a parked data plane; per-shard fd-keyed slot table, no locks). **Live-gated** (`just tls`, 5/0) from `.wo` against a local TLS 1.3 stub incl. untrusted-chain + hostname-mismatch negatives. Client side complete | jarvis's path; the reason the story exists | -| G — server (inbound) | 📋 **READY 2026-09-09** (see §G below) — the server handshake FSM, constant-time RSA-PSS + ECDSA-P256 **signing** (the first private-key ops), private-key parsing, `net.accept_tls`; porch terminates TLS | retires the inbound proxy requirement, and the doctrine docs; may become its own iteration | +| G — server (inbound) | ✅ **COMPLETE 2026-09-09** — the server handshake FSM (loopback-KAT'd), constant-time RSA-PSS + ECDSA-P256 **signing** (RFC 6979), private-key parse, `net.accept_tls` (id 118); live-gated by `openssl s_client` (EC + RSA), `just tls-server` 4/0 | retires the inbound proxy requirement; doctrine docs corrected | ## F3c-net — the socket/VM slice (✅ **LANDED 2026-09-09**; decisions locked, forks auto-approved, `review_pending`) @@ -292,10 +292,15 @@ when picked up. the G1 primitives (RSA-PSS or ECDSA + a DER SEQ{r,s} encoder). **KAT by loopback** — our client driver against our server driver, EC then RSA server identity, ESTABLISHED with an app round-trip both ways. test_tls 123, ASan clean. -- **G3 — `net.accept_tls` + the live gate.** The VM builtin (id 118, - `WO_B_MAX`→118) + the shard identity cache, gated live by **`openssl s_client`** - completing a handshake against our server and exchanging data — real-world - interop, the mirror of §F3c-net's `openssl s_server` gate. +- **G3 — `net.accept_tls` + the live gate.** ✅ **LANDED 2026-09-09** — the VM + builtin (id 118, `WO_B_MAX`→118), private-key PEM/DER parse (`wo_pkey_parse`, + PKCS#8/PKCS#1/SEC1), the per-shard identity cache, and the `wo_tls_conn` + refactor (negotiated app keys, not an embedded driver — read/write serve both + directions). **Live-gated** `just tls-server` (`docs/examples/tls-server`): + **`openssl s_client` validates our hand-rolled server (EC + RSA certs) and gets + the reply — 4/0**, and the outbound `just tls` stays 5/0. Interop fix: the + server loops past the client's change_cipher_spec, and `net.close` drains a TLS + conn before FIN so the reply is never lost to an RST. ### Acceptance criteria diff --git a/runtime/src/CODE-LOGIC.md b/runtime/src/CODE-LOGIC.md index 705c1b8..b7fb552 100644 --- a/runtime/src/CODE-LOGIC.md +++ b/runtime/src/CODE-LOGIC.md @@ -470,5 +470,17 @@ Any failure — DNS, connect, handshake, chain, or hostname — **traps `WO_T_IO loudly**, never a silent downgrade. The live gate is `just tls` (`scripts/tls-accept.sh`, `docs/examples/tls-client`): a `.wo` client against a local TLS 1.3 stub, happy path plus untrusted-chain and hostname-mismatch -negatives. The inbound server (phase G) and a park-based handshake are not built -yet. +negatives. + +The **inbound server** (phase G) mirrors the client: `wo_tls_server` (the +server FSM in `tls.c`), the runtime's first **private-key** ops in `crypto.c` +(constant-time RSA-PSS + ECDSA-P256 signing with an RFC 6979 nonce, and +`wo_pkey_parse` for PKCS#8/PKCS#1/SEC1 keys), and `net.accept_tls` (id 118) in +`sysio.c`. A connection slot (`wo_tls_conn`) stores the negotiated application +keys — not a driver — so `net.read_tls`/`net.write_tls` serve both directions +over the record layer; the handshake drivers are transient. `net.close` drains a +TLS conn before closing so the reply is never lost to an RST. Gate: `just +tls-server` (`docs/examples/tls-server`) — `openssl s_client` validates the +hand-rolled server (EC + RSA certs). Named follow-ups (not built): a park-based +handshake, a `TlsConn` language object, connection pooling, and sending a TLS +close_notify on shutdown.