Handoff: P23 → P25 → P26 — TLS, Production HTTPS, Structured Concurrency
Handoff Prompt
Copy this into a fresh Claude Code session:
There’s your handoff at docs/HANDOFF_P23_P25_P26.md.
The approved implementation plan is at ~/.claude/plans/glittery-sleeping-sonnet.md — read both before starting.
Key insight: the codebase already has OpenSSL FFI bindings (std/ffi/tls.qz), a high-level TLS API (std/net/tls.qz), a blocking TLS server (http_serve_tls), and structured concurrency primitives (scope, supervisor, cancel tokens). This work is INTEGRATION, not building from scratch.
The non-blocking TLS pattern is identical to the existing _async_http_recv in http_server.qz: SSL_accept/SSL_read/SSL_write → check SSL_ERROR_WANT_READ/WANT_WRITE → io_suspend(fd) → retry. The colorblind async mechanism handles io_suspend inside go-task $poll state machines.
Start with Phase 1, Step 1.1: tls_accept_async in std/net/tls.qz. REMEMBER THE Prime Directives!
Context
Quartz is a self-hosted systems language with an M:N work-stealing scheduler. Channel throughput is 22M msgs/sec. P22 graceful shutdown and P28 timers (sched_sleep, select timeout) are complete. The next production blockers are P23 (TLS), P25 (Production HTTPS), and P26 (Structured Concurrency).
Current state:
std/ffi/tls.qz(257 lines) — Raw OpenSSL 3.x FFI bindings: SSL_CTX_new, SSL_new, SSL_accept, SSL_read, SSL_write, SSL_shutdown, SSL_get_error, SSL_ERROR_WANT_READ (2), SSL_ERROR_WANT_WRITE (3), SNI, cert validationstd/net/tls.qz(523 lines) — High-level API: tls_connect, tls_accept, tls_read, tls_write, tls_close, TlsResult/TlsError typeshttp_serve_tls()in http_server.qz — BLOCKING TLS server (kqueue, not M:N)http_serve_opts()in http_server.qz — Production server with keep-alive, graceful shutdown, load-shedding, timeouts (NO TLS variant)_async_http_recv_timeout— Non-blocking socket I/O with io_suspend + deadline (THE PATTERN to replicate for TLS)scope(),go_cancellable(),cancel_token_*— Structured concurrency (uses OS threads, not go-tasks)mir_emit_cancel_checkfires at every async resume point (sched_sleep included)- Build system auto-links
-lssl -lcryptowhen IR references OpenSSL extern "C" defdoesn’t cross module boundaries — TLS FFI must be inlined in http_server.qz (established pattern at lines 66-110)
What to Build
Phase 1: P23 — Non-Blocking TLS (stdlib only, no fixpoint)
Step 1.1: tls_accept_async(ctx, fd) in std/net/tls.qz
Non-blocking SSL handshake: SSL_accept() → check SSL_get_error →
WANT_READ/WANT_WRITE → io_suspend(fd) → retry. Socket must be O_NONBLOCK.
Step 1.2: tls_read_async(stream, max) in std/net/tls.qz
Non-blocking SSL_read with io_suspend loop. Same pattern.
Step 1.3: tls_write_async(stream, data) + tls_write_all_async
Non-blocking SSL_write. Loop on partial writes.
Step 1.4: Timeout variants using io_suspend_timeout(fd, remaining_ms, events)
tls_read_async_timeout, tls_write_async_timeout
Step 1.5: tls_close_async(stream) — Non-blocking SSL_shutdown
Step 1.6: Integration test (spec/qspec/tls_async_spec.qz) Async TLS echo server + concurrent go-task clients, self-signed certs.
Phase 2: P25 — Production HTTPS Server (stdlib only, no fixpoint)
Step 2.1: _handle_tls_connection_keepalive in http_server.qz
TLS connection handler mirroring _handle_connection_keepalive.
Non-blocking handshake → parse request → call handler → write response.
Keep-alive, timeouts, clean shutdown.
Step 2.2: http_serve_tls_opts(config, tls_config, handler) in http_server.qz
Production HTTPS. Same architecture as http_serve_opts:
blocking accept → sched_spawn_priority per connection.
Add HttpsTlsConfig struct with cert_file/key_file.
Step 2.3: Async TLS request/response helpers
_async_tls_recv_timeout, _async_tls_parse_request_timeout,
_async_tls_write_response_timeout — all inline in http_server.qz
Step 2.4: HEAD/OPTIONS auto-handling (low complexity) Step 2.5: Chunked transfer encoding parsing (medium complexity) Step 2.6: Access logging — Apache combined format (low complexity)
Phase 3: P26 — Structured Concurrency with Go-Tasks (mostly stdlib)
Step 3.1: go_scope(body) in std/concurrency.qz
Nursery for go-tasks. Spawns via go_cancellable, awaits all children,
cancel-on-failure. Uses async/sched_spawn (not task_group/OS threads).
Step 3.2: go_supervisor(body) — Non-cancelling nursery
Step 3.3: go_scope_timeout(ms, body) — Deadline-bounded nursery
Step 3.4: sched_sleep cancel check — ALREADY DONE (mir_emit_cancel_check)
Step 3.5: go_race(tasks) — First-completed wins via go_cancel_on_success
Step 3.6: Lint rule: warn on bare go outside scope (tools/lint.qz)
Key Files
- std/net/tls.qz — Add async TLS functions (Phase 1)
- std/net/http_server.qz — Add http_serve_tls_opts (Phase 2)
- std/concurrency.qz — Add go_scope/go_supervisor/go_race (Phase 3)
- std/ffi/tls.qz — Reference: SSL_ERROR constants (read-only)
- spec/qspec/tls_async_spec.qz — New test file (Phase 1)
- spec/qspec/https_server_spec.qz — New test file (Phase 2)
- spec/qspec/go_scope_spec.qz — New test file (Phase 3)
Verification After Each Step
- Compile test file:
./self-hosted/bin/quartz -I std spec/qspec/NEW_SPEC.qz - Link:
llc -filetype=obj + clang -lssl -lcrypto -lpcre2-8 -lpthread - Run:
QUARTZ_COMPILER=./self-hosted/bin/quartz ./NEW_TEST - Regression: concurrency_spec.qz (57/57), sched_sleep_spec.qz (10/10)
- No fixpoint needed (all stdlib changes)
Critical Patterns to Follow
Non-blocking TLS (mirrors _async_http_recv at http_server.qz:1233):
while true
ret = SSL_read(ssl, buf, max)
if ret > 0: return ret
err = SSL_get_error(ssl, ret)
if err == WANT_READ or err == WANT_WRITE
io_suspend(fd)
else
return error
Inline FFI (extern “C” doesn’t cross modules): TLS FFI declarations must be duplicated in http_server.qz. See existing pattern at lines 66-110.
go_scope nursery (key difference from scope()):
go_scope do |nursery|
go_cancellable(task1, nursery.cancel_token)
go_cancellable(task2, nursery.cancel_token)
# All children awaited before scope exits
# If any child fails, remaining are cancelled
end
Current commit: d42f060 on trunk branch. CLAUDE.md for build commands. Approved plan at: ~/.claude/plans/glittery-sleeping-sonnet.md
Prime Directives: World-class only. No shortcuts. No silent compromises. Fill every gap.