quickable
Guides

Run it

The whole stack from one docker-compose file, behind one domain, with a smoke test that proves the pipeline end-to-end.

Start the stack

git clone <your-clone-url> quickable && cd quickable
docker compose -f deploy/compose.yaml up -d --build --wait

That is the entire deployment. What starts:

ServiceRole
caddyThe only published port (:8080 by default). Every other service lives on internal networks.
natsThe broker: JetStream, websocket, the decentralized-auth resolver.
bootstrapOne-shot: mints operator/system/platform credentials and the nats config onto the creds volume, then exits. Idempotent across restarts.
coreThe application flows. Stateless; scales with --scale core=N.
transformThe build worker — read-only rootfs, tmpfs scratch, nats-only network.
gatewayREST /v1, MCP /mcp, shells /a/…, bundles /bundles/….
victoria-logs / victoria-metricsLog and metric backends (operator-only, behind basic auth).

The single domain

Caddy routes everything on one hostname (deploy/Caddyfile):

PathWhere it goes
/v1/*REST projection of the API registry (gateway)
/mcpMCP streamable-HTTP endpoint (gateway)
/a/<ns>/<app>The rendered app shell (gateway)
/bundles/*Built app bundles (gateway → object store, read-only)
/natsWebsocket proxy to the broker — the native NATS path for browsers and scripts
/docs/*This documentation site (static files, zero runtime)
/o11y/logs*, /o11y/metrics*VictoriaLogs / VictoriaMetrics, basic-auth admin
/healthzGateway health
everything elsegateway (404s outside its sealed route inventory)

For a real hostname with automatic TLS set QUICKABLE_DOMAIN=docs.example.com (and expose 443); the default :8080 is plain HTTP for local runs. SIGNUP_MODE=invite gates signup behind invite tokens.

Serving this docs site

The /docs route serves static files from /srv/docs inside the caddy container. The export is produced by pnpm --filter @quickable/docs build into apps/docs/out/; provide it to caddy either as a read-only volume on the caddy service (../apps/docs/out:/srv/docs:ro — one line in deploy/compose.yaml) or by baking the directory into a caddy image at /srv/docs. Until the files are present the route simply 404s; nothing else depends on it (the site is an enumerated static surface, A2′).

Prove it works

The smoke test exercises the public edge only — signup, native ws connect, bearer mint, app create, material put, transform build, rendered shell, served bundle:

pnpm install && pnpm -r build
node packages/qctl/dist/index.js smoke run http://localhost:8080 /tmp/smoke.json

Every step prints PASS/FAIL. Afterwards, restart core (docker compose -f deploy/compose.yaml restart core) and re-verify that the same bearer and the same app survive a restart (A8 restart-idempotency, observed at the edge):

node packages/qctl/dist/index.js smoke verify http://localhost:8080 /tmp/smoke.json

Tear down

docker compose -f deploy/compose.yaml down -v

-v removes the named volumes (creds, nats-data, the Victoria stores) — the complete durable state (A8′). Ramp-to-zero leaves nothing behind.

On this page