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)
5.4 KiB
Story — porch, the writeonce web framework
The second track. Where
language-runtime-database/ grows
the language, this track grows the one library written in it:
porch, consumed by every serving sample
through wo.toml [deps].
Numbering restarts at 1 and is local to this track. Frontmatter carries
track: porch so a query over docs/stories/ can tell a porch iteration 3 from
a language iteration 3. Status rules are the repo's, unchanged: status: in
frontmatter is the only place state lives, no directory encodes it.
Why a separate track
Three reasons, all practical:
- Different substrate, different gates. porch is
.wosource. Its iterations are proven byjust web-appandjust site, never by the conformance corpus oroop-accept. Mixing them into the language track's sequence made both harder to read. - Different cadence. A porch slice is days; a language slice that touches
wob.hand the VM is longer and riskier. Interleaving them in one numbering forced false ordering decisions. - The framework is now the product surface.
writeonce.deis served by porch. Its gaps are what a visitor hits first, so they deserve a roadmap that is not buried behind runtime work.
The language track stays upstream: when a porch iteration needs a new builtin, that half is called out explicitly and the language track owns it.
Where the sequence came from
The Fiber v3.5.0 parity study
— gofiber/fiber read end to end against porch's actual .wo source: its routing
surface, Req/Res API, binder, lifecycle hooks and the Config of all 32 of
its middleware/ packages. Nine of those 32 already have a working porch
counterpart, so this is a breadth roadmap, not a rescue.
That study replaced language-track iteration 39, which is now a pointer here.
The sequence
Ordered by dependency, not by importance — and the first slice is deliberately the cheapest, so the store pattern and the gate shape are proven before the risky work starts.
| # | Iteration | Delivers | Needs |
|---|---|---|---|
| 1 | Store-backed middleware | rate limiting + idempotency over a @table store, serialized through a sharded actor pool |
🔄 in progress; needs no new primitive (call/send/monitor/time.after all landed) |
| 2 | Randomness and cookies | a random_bytes runtime builtin, repeated response headers, Cookie: parsing, signed cookies |
a language-track builtin (phase A) |
| 3 | Sessions | server-side sessions, idle + absolute timeout, revocation | 2 |
| 4 | CSRF | token mint/verify, trusted origins, single-use tokens | 2, 3 |
| 5 | Routing and response ergonomics | the remaining method helpers, named routes, per-route body limit, request ids, the missing response helpers | nothing — parallel to 2–4 |
| 6 | Streaming core | incremental response writes and chunked framing — the seam three iterations wait on | nothing new, but it changes Resp |
| 7 | SSE and compression | server-sent events, gzip/deflate | 6 |
| 8 | Static files and lifecycle | byte ranges, cache headers, directory listing, lifecycle hooks, the small middleware everyone ships | 6 |
1 ─ independent, start here
5 ─ independent, any time
2 ──▶ 3 ──▶ 4
6 ──▶ 7
└──▶ 8
What this track does NOT own
| Not porch's | Owner |
|---|---|
| typed binding of query/params/form into a class | language: @derive — reflection is forbidden by principle 13 |
TTL cache, transaction { }, durable job queue |
language: iteration 18 |
a proxy middleware |
language: iteration 38 — needs net.connect, ✅ landed 2026-09-07 (id 110; plus net.connect_tls for an HTTPS upstream, runtime-v2 9). Buildable now |
| metrics, profiling, per-change CI, fuzzing | runtime-v2 7 — observability (was language iteration 30; metrics/profiling/trace-on-trap; CI + fuzz are tooling, split out) |
| TLS | ✅ runtime-v2 9 — 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 |
Review protocol
Same as the language track: the developer reads one iteration, approves or
amends; the next starts only after approval. Each iteration is an unsplittable
value slice with phases, per-phase tasks, Given/When/Then acceptance criteria,
and an out-of-scope list. Every phase ends with both serving gates green —
just web-app and just site — because porch has two consumers and a change
that only satisfies one is not done.