Skip to content

feat(daemon): publishable Docker image + compose stack#36

Open
Codename-11 wants to merge 1 commit into
feature/v3-foundationfrom
feature/daemon-docker-image
Open

feat(daemon): publishable Docker image + compose stack#36
Codename-11 wants to merge 1 commit into
feature/v3-foundationfrom
feature/daemon-docker-image

Conversation

@Codename-11
Copy link
Copy Markdown
Owner

Summary

Adds the ghcr.io/axiom-labs/arc-daemon Docker image + compose example for headless ARC installs. Runs arc-daemon --foreground on :7272 as a non-root user with state mounted at /home/arc/.arc.

  • Multi-stage Dockerfile (packages/daemon/Dockerfile) — stage 1 installs a filtered pnpm subgraph (core, client, daemon), rebuilds better-sqlite3 against the container Node, runs tsup + pnpm deploy --prod. Stage 2 is node:20-bookworm-slim + tini + curl, non-root uid 1000, VOLUME /home/arc/.arc, HEALTHCHECK probing /health.
  • Standalone CLI (packages/daemon/src/cli.ts) — tiny flag parser (--port/--host/--arc-dir) used as the ENTRYPOINT and registered as the arc-daemon bin. The richer arc daemon start remains canonical.
  • tsup config (packages/daemon/tsup.config.ts) — daemon-scoped build that inlines workspace siblings and externalises real deps so Node can resolve CJS modules like ws + better-sqlite3 at runtime.
  • docker-compose.yml — loopback-only daemon service with a watchtower sidecar behind the auto-update profile.
  • DOCKER.md — operator guide (pull/run, port mapping, volume semantics, security notes).
  • GitHub Actions workflow (.github/workflows/docker-daemon.yml) — pushes to GHCR on v* tags and on workflow_dispatch; matrixed over linux/amd64 + linux/arm64 with a manifest-merge job.
  • Deps — daemon now explicitly declares @axiom-labs/arc-client and ws, which were previously leaking in as transitives.

Does NOT wire an external DB, change the default port (7272), or publish the image (CI does that on tag).

Test plan

  • pnpm install — clean
  • npx tsc --noEmit — passes
  • pnpm --filter @axiom-labs/arc-daemon build — produces dist/cli.js + dist/index.js
  • Standalone binary: node dist/cli.js --foreground --port 17272 --host 127.0.0.1 — daemon starts, curl /health returns {"ok":true,"protocol":1,...}, SIGTERM shuts down cleanly
  • docker build -f packages/daemon/Dockerfile -t arc-daemon:test .unverified locally: Docker Desktop's Linux engine isn't running in this worktree. CI workflow will exercise it on the first manual dispatch.
  • docker run + /health round-trip — unverified locally (same reason)

Notes for reviewers

  • The --foreground flag is accepted by the standalone bin for parity with arc daemon start, but the bin is always foreground (no daemonisation). That's documented in --help and in DOCKER.md.
  • Inside the container ARC_HOST=0.0.0.0 binds all interfaces, but the daemon's existing Host-header allowlist still requires 127.0.0.1 / localhost. Publishing with -p 127.0.0.1:7272:7272 (the documented default) keeps access loopback-only on the host. LAN exposure requires a proxy that rewrites Host — called out in DOCKER.md.
  • pnpm deploy --prod --legacy is used to produce a flat production tree without workspace symlinks. The --legacy flag is required on pnpm 10 because injected-workspace-packages conflicts with --prod pruning.

Ships `ghcr.io/axiom-labs/arc-daemon` for headless installs that want
a persistent daemon without the full CLI. The image runs
`arc-daemon --foreground` on :7272 as a non-root user (uid 1000) with
state at /home/arc/.arc, signals handled via tini, and a /health-based
HEALTHCHECK.

Adds:
- packages/daemon/src/cli.ts — standalone foreground entrypoint with a
  tiny flag parser (--port/--host/--arc-dir). Registered as
  `arc-daemon` bin and used as the image ENTRYPOINT.
- packages/daemon/tsup.config.ts — daemon-scoped build that inlines
  workspace siblings and externalises real deps (ws, better-sqlite3,
  zod) so Node's loader resolves CJS modules at runtime.
- packages/daemon/Dockerfile — multi-stage (node:20-bookworm-slim).
  Stage 1 installs a filtered pnpm subgraph, rebuilds better-sqlite3,
  runs `pnpm deploy --prod` for a pruned tree. Stage 2 is tini + curl
  + non-root user + VOLUME /home/arc/.arc.
- packages/daemon/.dockerignore — shrinks the build context.
- packages/daemon/docker-compose.yml — loopback-only daemon service
  with an opt-in watchtower sidecar behind the `auto-update` profile.
- packages/daemon/DOCKER.md — operator guide: pull/run, port mapping,
  volume semantics, security notes (loopback default, proxy for LAN).
- .github/workflows/docker-daemon.yml — GHCR publish on v* tags plus
  workflow_dispatch, matrixed over linux/amd64 + linux/arm64 with a
  manifest-merge job.
- packages/daemon/package.json — adds `@axiom-labs/arc-client` and
  `ws` as explicit deps (previously transitive through client) and
  registers `arc-daemon` as a bin entry.

E2E locally: `npx tsc --noEmit` passes and the built CLI serves the
expected `{"ok":true,"protocol":1,...}` payload on /health. Full
`docker build` unverified here — Docker Desktop's Linux engine isn't
running in this worktree; CI will exercise the full path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant