Fleet packaging
TL;DR: the trip2g image ships two static binaries — /trip2g (the primary, CMD) and /fleet (the agent host, secondary). The base image is LLM-only: no language interpreters. If a role uses executor: code, you add the interpreter yourself — extend the image or copy the static /fleet binary into your own runtime. This keeps the default image small and the code-execution surface an explicit operator choice (it's off by default anyway — --allowed-programs is empty).
One image, two binaries
The main Dockerfile builds both binaries (CGO_ENABLED=0, fully static) into one Alpine image:
/trip2g # CMD — the server
/fleet # secondary — run as a sidecar / second container
Run trip2g normally; run the fleet from the same image by overriding the command:
# docker-compose
services:
trip2g:
image: ghcr.io/trip2g/trip2g:latest # CMD ["/trip2g"]
fleet:
image: ghcr.io/trip2g/trip2g:latest
command: ["/fleet"]
environment:
- TRIP2G_FLEET_TRIP2G_URL=http://trip2g:20081
- TRIP2G_FLEET_CALLBACK_URL=http://fleet:9090
- TRIP2G_FLEET_JWT_SECRET=... # = the app's JWT secret
- TRIP2G_FLEET_LLM_BASE_URL=...
- TRIP2G_FLEET_LLM_API_KEY=...
# ... see cmd/fleet flags; every flag has a TRIP2G_FLEET_<NAME> env
trip2g stays a dumb event source; the fleet reconciles its own webhooks against it. One image is the whole unit.
Enabling executor: code
A role with executor: code runs code through an interpreter (python, node, bash…). The base image has none. Two ways to add them — both rely on the binary being static, so it drops into any runtime:
A. Extend the image (simplest):
FROM ghcr.io/trip2g/trip2g:latest
RUN apk add --no-cache python3 # or nodejs, bash, ...
# run with --allowed-programs python (or TRIP2G_FLEET_ALLOWED_PROGRAMS=python)
CMD ["/fleet"]
B. Copy the static binary into your own runtime (when you want a different base):
FROM python:3.12-alpine
COPY --from=ghcr.io/trip2g/trip2g:latest /fleet /usr/local/bin/fleet
ENTRYPOINT ["fleet"]
Then opt code execution in explicitly:
--allowed-programs python,node # default empty = code execution disabled
Keep the interpreter set minimal, and isolate the fleet container (network policy, read-only FS, non-root) — see process_isolation. The base image carries no interpreters precisely so you make this choice deliberately, per deployment.