# hermes-box-lite on Docker — everything baked into one image at build time.## The whole provisioning story (node, uv, claude, codex, hermes, executor,# guest assets, /data skeleton) is the existing provision/provision.sh, run# once at BUILD time instead of streamed into a live VM on every `box new`.# Creating a box becomes `docker run` + named volume: seconds, not minutes,# and every box is bit-identical to the image it came from.## /data is declared a VOLUME after the skeleton is seeded, so a fresh named# volume is auto-populated from the image on first mount (Docker copies the# image's mount-point contents into an empty named volume).## Build from the repository root:# docker build -t hermes-box-lite -f docker/Dockerfile .FROM ubuntu:24.04SHELL ["/bin/bash", "-o", "pipefail", "-c"]# Two provisioning layers so day-to-day guest-script edits don't re-run the# multi-minute tool installs: `tools` (node/uv/claude/codex/hermes/executor)# caches until box.env or provision.sh changes; `assets` (hb, profiles, /data# skeleton) rebuilds in seconds when guest/ changes.COPY box.env /tmp/ctx/box.envCOPY provision/ /tmp/ctx/provision/RUN bash /tmp/ctx/provision/provision.sh toolsCOPY guest/ /tmp/ctx/guest/RUN bash /tmp/ctx/provision/provision.sh assets && rm -rf /tmp/ctxCOPY docker/entrypoint.sh /opt/hermes-box/bin/entrypointCOPY docker/executor-entrypoint.sh /opt/hermes-box/bin/executor-entrypointRUN chmod 0755 /opt/hermes-box/bin/entrypoint /opt/hermes-box/bin/executor-entrypoint# Durable state. Everything else in the container is disposable image content.VOLUME /data# Fixed internal bridge ports (hb-workload's socat bridges bind 0.0.0.0 here# and forward to the loopback-only services). Hosts publish these with -p and# let Docker pick free host ports — no port registry, no collision logic.EXPOSE 14788 18642ENTRYPOINT ["/opt/hermes-box/bin/entrypoint"]
# hermes-box-lite on Docker — everything baked into one image at build time.
#
# The whole provisioning story (node, uv, claude, codex, hermes, executor,
# guest assets, /data skeleton) is the existing provision/provision.sh, run
# once at BUILD time instead of streamed into a live VM on every `box new`.
# Creating a box becomes `docker run` + named volume: seconds, not minutes,
# and every box is bit-identical to the image it came from.
#
# /data is declared a VOLUME after the skeleton is seeded, so a fresh named
# volume is auto-populated from the image on first mount (Docker copies the
# image's mount-point contents into an empty named volume).
#
# Build from the repository root:
# docker build -t hermes-box-lite -f docker/Dockerfile .
FROM ubuntu:24.04
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Two provisioning layers so day-to-day guest-script edits don't re-run the
# multi-minute tool installs: `tools` (node/uv/claude/codex/hermes/executor)
# caches until box.env or provision.sh changes; `assets` (hb, profiles, /data
# skeleton) rebuilds in seconds when guest/ changes.
COPY box.env /tmp/ctx/box.env
COPY provision/ /tmp/ctx/provision/
RUN bash /tmp/ctx/provision/provision.sh tools
COPY guest/ /tmp/ctx/guest/
RUN bash /tmp/ctx/provision/provision.sh assets && rm -rf /tmp/ctx
COPY docker/entrypoint.sh /opt/hermes-box/bin/entrypoint
COPY docker/executor-entrypoint.sh /opt/hermes-box/bin/executor-entrypoint
RUN chmod 0755 /opt/hermes-box/bin/entrypoint /opt/hermes-box/bin/executor-entrypoint
# Durable state. Everything else in the container is disposable image content.
VOLUME /data
# Fixed internal bridge ports (hb-workload's socat bridges bind 0.0.0.0 here
# and forward to the loopback-only services). Hosts publish these with -p and
# let Docker pick free host ports — no port registry, no collision logic.
EXPOSE 14788 18642
ENTRYPOINT ["/opt/hermes-box/bin/entrypoint"]