Files
rose-ash/docker-compose.dev-sx-host.yml
giles afb9ce5e90 TA-live: real A→B federation over HTTP + a durable outbox (LIVE-VERIFIED)
Step 3 — federation, live-verified with TWO real host instances.

- host/ta.sx: host/ta--post/make-http-wire/federate (POST a serialized activity to a peer's /inbox
  over real HTTP). host/blog.sx: POST /inbox (host/blog-inbox → receive! → process locally, does NOT
  re-federate — no loops).
- DURABLE OUTBOX (fed-sx reliability, after the user asked 'if B is down does it still work?'):
  emit! processes locally (always succeeds), QUEUES per-peer to a persisted outbox, delivers
  best-effort. A peer being DOWN no longer fails the publish — delivery is GUARDED (SX guard catches
  the http-request connection error), failed items stay queued and retry on next emit / on boot /
  manual /flows?flush=1. /flows shows the outbox depth.
- serve.sh: SX_PEERS → peers; boot load+flush of the outbox. docker-compose: a 2nd host sx_host_b
  (peer B, own store, no peers).

LIVE PROOF: (1) a peer POSTs create/article to blog.rose-ash.com/inbox → A fires validate+notify.
(2) publish on A → federates to B → B fires ITS behaviors on A's activity (B's /flows + /activities).
(3) RESILIENCE: publish with B DOWN → A returns 303 (was 500) + queues; start B + flush → B receives
the backlog + fires. blog 218/218 (+TA receive test), full host conformance green.

A = blog.rose-ash.com (public/Caddy); B = sx_host_b (internal docker DNS only, no public domain).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 19:47:07 +00:00

117 lines
4.9 KiB
YAML

# host-on-sx live service — the SX web host (lib/host) served by the native
# http-listen server via lib/host/serve.sh. Joins the sx-dev project + externalnet
# so Caddy can reverse_proxy a subdomain to it (blog.rose-ash.com). Isolated from
# the sx_docs server: separate container, separate port.
#
# Usage:
# docker compose -p sx-dev -f docker-compose.dev-sx-host.yml up -d sx_host
# docker compose -p sx-dev -f docker-compose.dev-sx-host.yml logs -f sx_host
# docker compose -p sx-dev -f docker-compose.dev-sx-host.yml down
services:
sx_host:
image: registry.rose-ash.com:5000/sx_docs:latest
container_name: sx-dev-sx_host-1
entrypoint: ["bash", "/app/lib/host/serve.sh"]
working_dir: /app
environment:
SX_PROJECT_DIR: /app
SX_SERVER: /app/bin/sx_server
HOST_PORT: "8000"
# Bind all interfaces so Caddy (on externalnet) can reach it.
SX_HTTP_HOST: "0.0.0.0"
# Durable persist store root — on a named volume so data survives restarts.
SX_PERSIST_DIR: /data/persist
# Blog write auth: admin login + session-cookie signing secret. The blog
# write routes (POST /new, POST/PUT/DELETE /posts) are guarded by a session
# login or Bearer token, so these gate publishing. Not a real site — these
# are demo creds; rotate by editing here and recreating the container.
SX_ADMIN_USER: admin
SX_ADMIN_PASSWORD: "sx-host-camper-van-2026"
SX_SESSION_SECRET: "ra-host-sess-7c1f9b3e2a8d4056"
# Serving-mode JIT: bytecode-compile hot SX (esp. the Datalog/relations path)
# on the epoch serving channel. Validated: host conformance 271/271 under JIT,
# 5.4x faster (1m43s -> 19s). Default-OFF gate, opt in here.
SX_SERVING_JIT: "1"
OCAMLRUNPARAM: "b"
# TA-live: federate emitted activities to peer B's /inbox (real fed-sx over HTTP).
SX_PEERS: "http://sx_host_b:8000"
volumes:
# SX source (hot-reload on container restart)
- ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./next:/app/next:ro
- ./web:/app/web:ro
# Client assets for the blog SPA: the WASM OCaml kernel + sx-platform + the
# web-stack modules, served by lib/host/static.sx at /static/**.
- ./shared/static:/app/shared/static:ro
# OCaml server binary — this worktree's build (has the SX_HTTP_HOST bind fix)
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
# Durable persist store (the SX op-log/kv on disk) — survives restarts.
# Host dir, chowned to the image's appuser (uid 10001) so the non-root
# server can write: sudo mkdir -p /root/sx-host-persist && sudo chown 10001:10001 /root/sx-host-persist
- /root/sx-host-persist:/data/persist
networks:
- externalnet
- default
restart: unless-stopped
# The durable-execution KERNEL (next/kernel/host_kernel.erl) — a persistent next/ service holding
# flow_store across requests (RA-live substrate). The host reaches it at http://sx_kernel:8930 over
# the shared `default` network. SX_HTTP_HOST=0.0.0.0 so the bind is reachable cross-container.
sx_kernel:
image: registry.rose-ash.com:5000/sx_docs:latest
container_name: sx-dev-sx_kernel-1
entrypoint: ["bash", "/app/next/kernel/serve.sh"]
working_dir: /app
environment:
SX_PROJECT_DIR: /app
SX_SERVER: /app/bin/sx_server
KERNEL_PORT: "8930"
SX_HTTP_HOST: "0.0.0.0"
OCAMLRUNPARAM: "b"
volumes:
- ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./next:/app/next:ro
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
networks:
- default
restart: unless-stopped
# A second host instance — a federation PEER (B). Host A federates its emitted activities to B's
# /inbox; B's engine fires ITS OWN behaviors on A's state changes ("everything works over fed-sx").
# B has its own durable store + no peers (receives without re-federating). Reached on the default
# network only (not exposed via Caddy).
sx_host_b:
image: registry.rose-ash.com:5000/sx_docs:latest
container_name: sx-dev-sx_host_b-1
entrypoint: ["bash", "/app/lib/host/serve.sh"]
working_dir: /app
environment:
SX_PROJECT_DIR: /app
SX_SERVER: /app/bin/sx_server
HOST_PORT: "8000"
SX_HTTP_HOST: "0.0.0.0"
SX_PERSIST_DIR: /data/persist
SX_ADMIN_USER: admin
SX_ADMIN_PASSWORD: "sx-host-b-camper-2026"
SX_SESSION_SECRET: "ta-host-b-sess-9d2e1f"
SX_SERVING_JIT: "1"
OCAMLRUNPARAM: "b"
volumes:
- ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./next:/app/next:ro
- ./web:/app/web:ro
- ./shared/static:/app/shared/static:ro
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- /root/sx-host-b-persist:/data/persist
networks:
- default
restart: unless-stopped
networks:
externalnet:
external: true