Quartz v5.25

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 validation
  • std/net/tls.qz (523 lines) — High-level API: tls_connect, tls_accept, tls_read, tls_write, tls_close, TlsResult/TlsError types
  • http_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_check fires at every async resume point (sched_sleep included)
  • Build system auto-links -lssl -lcrypto when IR references OpenSSL
  • extern "C" def doesn’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

  1. Compile test file: ./self-hosted/bin/quartz -I std spec/qspec/NEW_SPEC.qz
  2. Link: llc -filetype=obj + clang -lssl -lcrypto -lpcre2-8 -lpthread
  3. Run: QUARTZ_COMPILER=./self-hosted/bin/quartz ./NEW_TEST
  4. Regression: concurrency_spec.qz (57/57), sched_sleep_spec.qz (10/10)
  5. 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.