fabro/docker/split-web/README.md
Bryan Helmkamp 9d9c48ba25
feat(server): add docker worker runtime
Add server worker runtime settings and the worker bootstrap API.

Add API bootstrap support for worker CLI processes and Docker-backed workers.

Update the split-web PoC compose config and docs for Docker worker validation.
2026-05-29 21:54:01 -04:00

2.9 KiB

Split Web Compose PoC

This Compose stack proves that Fabro can serve the React SPA from a separate static process while the Rust server remains the API and browser-auth origin.

Request ownership:

  • /api/* -> fabro-api:32276
  • /auth/* -> fabro-api:32276
  • /health -> fabro-api:32276
  • everything else -> fabro-web:80

The Rust server still contains bundled SPA assets. In this PoC they are simply not reachable through the edge service for normal web paths.

The edge proxy adds X-Fabro-PoC-Upstream to responses so manual checks can confirm which service handled a request.

Run

Build the local Fabro image from the current tree:

cargo dev docker-build --tag fabro-sh/fabro:split-web-poc

Set local auth secrets:

export SESSION_SECRET="$(openssl rand -hex 32)"
export FABRO_DEV_TOKEN="fabro_dev_$(openssl rand -hex 32)"

Start the split stack:

docker compose -f docker-compose.split-web.yaml up

Open http://localhost:8080.

Use SPLIT_WEB_PORT to expose a different local port, or FABRO_IMAGE to use a different API image.

Docker Workers

The API service mounts /var/run/docker.sock so it can create sibling worker containers on the host Docker daemon. Those workers join the stable fabro-split-web network and call the API at http://fabro-api:32276.

Worker containers receive their config and scoped secrets from the API bootstrap endpoint. They do not mount the server storage volume or /config/settings.toml.

This PoC sets remove_on_exit = false so QA can inspect a stopped worker container after a run. Production examples should omit that setting and keep the default true, because retained worker files include the worker-local bootstrap config and vault.

Validate

curl -i http://localhost:8080/health
curl -i http://localhost:8080/api/v1/health
curl -I http://localhost:8080/runs
curl -I http://localhost:8080/assets/app.css

curl -c /tmp/fabro.cookies \
  -H "content-type: application/json" \
  -d "{\"token\":\"$FABRO_DEV_TOKEN\"}" \
  http://localhost:8080/auth/login/dev-token

curl -b /tmp/fabro.cookies http://localhost:8080/api/v1/auth/me

Expected results:

  • /runs and /assets/* are served by the static fabro-web container.
  • /api/*, /auth/*, and /health are served by the Rust fabro-api container through the same browser origin.
  • Dev-token login sets a same-origin session cookie, and /api/v1/auth/me accepts it.

After starting a workflow run through the UI or CLI, verify that the API created a sibling worker container:

docker ps -a \
  --filter label=sh.fabro.managed=true \
  --filter label=sh.fabro.role=worker \
  --format 'table {{.Names}}\t{{.Status}}\t{{.Labels}}'

Expected evidence:

  • A fabro-worker-<run-id>-<suffix> container exists.
  • The container has sh.fabro.role=worker and sh.fabro.run_id=<run-id> labels.
  • With remove_on_exit = false, exited workers remain visible for inspection.