- jarvis (00-story): 6th track, 2nd software built with writeonce — an AI assistant; direct-HTTPS design; blockers named (net.connect + TLS) - runtime-v2 7 observability + 8 symmetric cipher: moved from the language track (were 30/43); 9 in-process TLS: created from the gap jarvis surfaces, RETIRES the "TLS is the proxy's job" doctrine (both directions) - language 41 (arena hang): fix design to ready — marshal cross-shard messages (root), align the shard_id % nshards route/compare + assert bound; poison-on-free + minimal fixture as follow-ups - fiber scope-gap analysis (plan/exploration/fiber/01): porch vs fiber, what porch lacks, would developers prefer porch - board + dependency-graph synced (porch 2-8 ready; rv2 table; §5/§5a graphs) (cherry picked from commit 203470ceb2a151fe3584931cd4237af3f96a9f29)
87 lines
4.5 KiB
Markdown
87 lines
4.5 KiB
Markdown
---
|
|
track: runtime-v2
|
|
iteration: "8"
|
|
status: pending
|
|
readiness: refine
|
|
---
|
|
|
|
# runtime-v2 8 — a symmetric cipher: authenticated encryption for cookies and data at rest
|
|
|
|
> Created 2026-09-06 from the porch-vs-fiber scope-gap analysis
|
|
> ([exploration](../../plan/exploration/fiber/01-porch-vs-fiber-scope-gap.md)) as
|
|
> a runtime-v2 iteration — a hand-rolled cipher builtin with a `types.ml` row is
|
|
> exactly the track's builtin-sized-seam shape (the iteration 42 precedent). (It
|
|
> is not a language-track iteration; the language number 43 was already spent on
|
|
> the wmux foundation.) **`readiness: refine`** — the
|
|
> gap, its consumer and its forks are named; not brainstormed to `ready`.
|
|
|
|
## Why this exists
|
|
|
|
The runtime has digests only — SHA-1, SHA-256, HMAC-SHA256, base64
|
|
([iteration 34](../language-runtime-database/34-crypto-builtins.md)) — and, from the porch track,
|
|
`random_bytes` ([porch 2](../porch/02-randomness-and-cookies.md)). Those let a
|
|
program *authenticate* and *sign* a value, and *mint* a random one. None of them
|
|
let it *encrypt* — turn a plaintext into a ciphertext only the key-holder can
|
|
read.
|
|
|
|
That absence is a named porch limit:
|
|
[porch 2](../porch/02-randomness-and-cookies.md) scopes out **encrypted
|
|
cookies** explicitly — "Fiber's `encryptcookie` needs a symmetric cipher, and
|
|
the runtime has digests only. Signed-and-readable is honest and sufficient for a
|
|
session id; encrypting a payload is a separate ask with a separate primitive
|
|
behind it." This is that separate primitive.
|
|
|
|
Signed-and-readable (what porch has) is correct for a session id — the client
|
|
may see it, it just may not forge it. Encryption is for the case where the
|
|
*payload itself* must be hidden from the client: an encrypted cookie carrying
|
|
app state, or a database field encrypted at rest.
|
|
|
|
## What it should deliver (scope to be refined)
|
|
|
|
- **An AEAD primitive** — authenticated encryption with associated data — as one
|
|
or two builtins in the crypto family beside `hmac_sha256`: encrypt (key,
|
|
nonce, associated-data, plaintext) → ciphertext+tag, and decrypt returning the
|
|
plaintext or nil on any authentication failure. AEAD, not a bare cipher,
|
|
because unauthenticated encryption is a footgun that ships.
|
|
- **The porch consumer**: an `encryptcookie`-equivalent — a cookie whose value is
|
|
encrypted, not merely signed — layered on iteration 2's cookie machinery.
|
|
|
|
## Forks the brainstorm must settle
|
|
|
|
1. **Which cipher? This is the load-bearing fork.** AES-256-GCM is what browsers,
|
|
fiber and every peer expect — but constant-time AES in pure software (no
|
|
AES-NI intrinsics) is genuinely hard to get right. ChaCha20-Poly1305
|
|
(RFC 8439) is modern, is far easier to implement constant-time in portable C,
|
|
and is what a from-scratch no-dependency runtime should probably prefer — at
|
|
the cost of being less "expected." The runtime hand-rolls its crypto (the
|
|
SHA-256 precedent, no external dependency), which weighs toward ChaCha.
|
|
2. **Nonce management.** A reused nonce is catastrophic for both GCM and ChaCha.
|
|
Caller-supplied nonces put that footgun in every app; a builtin-generated
|
|
random nonce (drawing on iteration 2's `random_bytes`, prepended to the
|
|
ciphertext, as fiber's `NewGCMWithRandomNonce` does) removes it. Leaning
|
|
builtin-generated — so this iteration is ordered after porch 2's builtin.
|
|
3. **Key handling.** A raw 32-byte key (from `random_bytes`, carried as base64 in
|
|
config, the `encryptcookie.GenerateKey` shape) with a length check, versus a
|
|
passphrase-plus-KDF. Leaning raw key with validation; a KDF is its own ask.
|
|
4. **Hand-roll versus vendor.** Doctrine is no external dependencies. A
|
|
hand-rolled ChaCha20-Poly1305 in C is bounded and well-specified; hand-rolled
|
|
AES-GCM is more error-prone. This fork is the practical face of fork 1.
|
|
|
|
## Out of scope
|
|
|
|
- **Asymmetric crypto** (RSA, ECDH, signatures beyond HMAC). A different, much
|
|
larger surface with no current consumer.
|
|
- **Key rotation, a KMS, envelope encryption.** Operational key management is its
|
|
own story if a consumer appears.
|
|
- **TLS.** Proxy-terminated by doctrine; this cipher is for application payloads,
|
|
not the transport.
|
|
- **Compression before encryption** (the CRIME/BREACH interaction). A caller
|
|
concern to document, not a primitive.
|
|
|
|
## Info
|
|
|
|
One named consumer today (encrypted cookies), with database-field-at-rest as a
|
|
plausible second — enough to not be decoration, not so much as to over-build.
|
|
Depends on iteration 2's `random_bytes` (for the nonce, fork 2) and extends
|
|
iteration 34's crypto builtins. Pure compute — no actors, not exposed to the
|
|
lang-41 hang.
|