Gap analysis from the five-subsystem set (acl/feed/flow/mod/search): - store-on-sx: event-sourcing foundation the others fake with in-memory lists (build first) - commerce-on-sx: catalog/cart/pricing/orders on miniKanren (+ store + flow) - identity-on-sx: OAuth2/sessions/membership on Erlang (the core acl assumes) - content-on-sx: documents/blocks/CRDT on Smalltalk - events-on-sx: calendar/ticketing on Datalog + flow-driven delivery - host-on-sx: the web boundary — off Quart onto native server+SXTP now, dream-on-sx next All DRAFT outlines; substrate choices proposed, not final. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
5.1 KiB
Markdown
101 lines
5.1 KiB
Markdown
# host-on-sx: The SX web host — off Quart, onto the kernel (Dream-bound)
|
||
|
||
> **DRAFT outline.** The integration boundary that turns the subsystem libraries
|
||
> into running services, and the strangler path off Python/Quart. This is the
|
||
> dependency hub — it imports every subsystem. Decision recorded below: native
|
||
> server + SXTP **now**, `dream-on-sx` framework layer **next**, Python only at the
|
||
> external-integration edges.
|
||
|
||
The subsystems (`feed`, `search`, `acl`, `mod`, `flow`, `commerce`, `identity`,
|
||
`content`, `events`) are libraries. Something has to receive an HTTP request, route
|
||
it, call the right subsystem, and serialize the response. Today that's Python/Quart
|
||
— the one large non-SX component in the stack: separate runtime, deploy, and
|
||
failure mode. The goal is to move the web/host/domain layer onto the SX substrate
|
||
and retire Quart, **incrementally (strangler-fig), never big-bang.**
|
||
|
||
This is already underway: a native OCaml HTTP server is live in prod on
|
||
`sx.rose-ash.com` (~3ms cached, ~323 req/s, ~2MB RSS), `defhandler`/`defpage`
|
||
exist, and a partial **SXTP** protocol is specced. That is the unblocked near-term
|
||
host — no `ocaml-on-sx` dependency.
|
||
|
||
## Two layers, two timelines
|
||
|
||
1. **Now (unblocked): native server + SXTP adapter + SX handlers.** Route rose-ash
|
||
endpoints onto the SX host one at a time. Each migrated endpoint is an SX
|
||
handler dispatching to a subsystem; Quart proxies the rest until cut over.
|
||
2. **Next: `dream-on-sx` as the framework layer.** Dream gives Quart-grade
|
||
ergonomics — typed routing, middleware stacks, sessions, CSRF. It is gated on
|
||
`ocaml-on-sx` Phases 1–5 + minimal stdlib. **This plan is the concrete target
|
||
user that un-parks `dream-on-sx`** (see `plans/dream-on-sx.md`): "the subsystems
|
||
need an HTTP front door" is the real feature pulling Dream. Until then, do not
|
||
block migration on Dream — the native server is sufficient.
|
||
3. **Always: Python only at the edges.** External integrations — SumUp payments,
|
||
Ghost CMS, ActivityPub crypto, IPFS/Kubo — ride Python libraries today. They
|
||
stay as thin injected adapters (Python/FFI) behind subsystem interfaces until
|
||
native replacements exist. "Drop Quart" ≠ "drop every line of Python."
|
||
|
||
## Status (rolling)
|
||
|
||
`bash lib/host/conformance.sh` → **0/0** (not yet started)
|
||
|
||
## Ground rules
|
||
|
||
- **Scope:** `lib/host/**` and `plans/host-on-sx.md`. May **import** every subsystem
|
||
+ the kernel's server/SXTP surface. Do **not** edit `spec/`, `hosts/`, `shared/`,
|
||
or subsystem internals — wire to their public APIs only. Host-primitive / server
|
||
changes belong in `hosts/` (out of scope) → Blockers.
|
||
- **Architecture:** a route maps (method, path) → handler; a handler is an SX fn
|
||
`request -> response` that calls subsystem APIs; middleware is composed handlers
|
||
(auth via `identity`, permission via `acl`, mute via subsystem prefs). SXTP is the
|
||
wire format between host and subsystem-as-service.
|
||
- **Migration discipline:** each endpoint moved must be behavior-equivalent to its
|
||
Quart original (golden-response test before flip). Keep a migration ledger.
|
||
- **Commits:** one feature per commit. Progress log + tick boxes.
|
||
|
||
## Architecture sketch
|
||
|
||
```
|
||
HTTP request HTTP response
|
||
│ ▲
|
||
▼ │
|
||
native OCaml http server (prod) ──────► lib/host/router.sx
|
||
(hosts/ — out of scope) — (method,path) → handler
|
||
│ ▲
|
||
▼ │
|
||
lib/host/middleware.sx lib/host/handler.sx
|
||
— auth(identity) ∘ acl ∘ mute ∘ ... — request → subsystem call → response
|
||
│ ▲
|
||
▼ │
|
||
lib/host/sxtp.sx subsystem APIs (feed/search/commerce/…)
|
||
— wire format, host↔service — called via public interfaces
|
||
│
|
||
└── external edges: SumUp / Ghost / AP / IPFS → injected Python/FFI adapters
|
||
```
|
||
|
||
## Phase 1 — Router + handler + one real endpoint
|
||
- [ ] `router.sx` — route table, (method,path) match
|
||
- [ ] `handler.sx` — request/response model, subsystem dispatch
|
||
- [ ] migrate ONE read endpoint (e.g. a feed timeline) end-to-end, golden test
|
||
- [ ] `conformance.sh` + scoreboard
|
||
|
||
## Phase 2 — Middleware + SXTP
|
||
- [ ] `middleware.sx` — composable auth/acl/mute/error layers
|
||
- [ ] `sxtp.sx` — host↔subsystem wire format (align with existing spec)
|
||
- [ ] migrate a write endpoint (auth + permission + action)
|
||
|
||
## Phase 3 — Strangler migration ledger
|
||
- [ ] enumerate Quart endpoints; track migrated vs proxied
|
||
- [ ] golden-response harness vs the live Quart responses
|
||
- [ ] cut over a whole domain (smallest: `likes` or `relations`) as proof
|
||
|
||
## Phase 4 — Dream framework layer (gated)
|
||
- [ ] gate: `ocaml-on-sx` Phases 1–5 + minimal stdlib green
|
||
- [ ] adopt `dream-on-sx` routing/middleware/session ergonomics over the same handlers
|
||
- [ ] re-home external adapters as native where replacements land
|
||
|
||
## Progress log
|
||
(loop fills this in)
|
||
|
||
## Blockers
|
||
(loop fills this in)
|