# syntax=docker/dockerfile:1.6 # # DECT Relay Agent — production container image # # Build context: the bundle produced by scripts/packageDectRelayAgent.js. # The bundle contains a `workspace/` directory that mirrors just enough of # the parent repo to satisfy the agent's `../integrations/...` and # `../utils/...` imports without any source rewriting: # # workspace/ # dect-relay-agent/ ← WORKDIR at runtime # package.json # index.js # integrations/cisco-dect/{client,probes,statusXml}.js # utils/httpDigestAuth.js # # Building this Dockerfile in the raw repo (`docker build dect-relay-agent/`) # WILL NOT WORK — the shared modules live one directory up and would be # outside the build context. Always build from a bundle produced by the # packager. # ── Stage 1: install prod deps ────────────────────────────────────── # node:20-alpine keeps the final image ~55MB. Alpine's musl libc has # been fine for this agent's plain JS + axios + ws footprint (no # native modules) but if you ever add one that needs glibc, switch to # node:20-slim. FROM node:20-alpine AS deps WORKDIR /build # Only copy the agent's manifest first so this layer caches across # code-only changes. COPY workspace/dect-relay-agent/package.json ./package.json # `npm install --omit=dev` because there's no committed lockfile # (the agent has three dependencies; every deploy resolving the same # `^` ranges is acceptable for this operational tool). Add # --ignore-scripts to refuse arbitrary lifecycle-script execution from # the registry — none of our current deps use lifecycle scripts. RUN npm install --omit=dev --ignore-scripts \ && npm cache clean --force # ── Stage 2: runtime ──────────────────────────────────────────────── FROM node:20-alpine AS runtime # tini gives us proper PID-1 signal handling (SIGTERM propagates # cleanly to node so our graceful shutdown path in index.js actually # runs on `docker stop`). RUN apk add --no-cache tini # Non-root user. UID/GID pinned so bind-mounted volumes (if any) are # predictable across hosts. RUN addgroup -S -g 1500 dect \ && adduser -S -u 1500 -G dect -H -s /sbin/nologin dect WORKDIR /app # Copy the shared workspace tree — the agent's imports of # `../integrations/...` and `../utils/...` resolve exactly as they do # in the source repo. See the bundle layout comment at the top of # this file. COPY --chown=dect:dect workspace/ ./ # Bring in the deps that stage 1 resolved. COPY --from=deps --chown=dect:dect /build/node_modules ./dect-relay-agent/node_modules USER dect WORKDIR /app/dect-relay-agent # Runtime config comes from `docker compose` (--env-file .env) or # `docker run --env-file ...`. Never bake secrets into the image. # The agent process itself validates required vars and exits 1 if any # are missing (see assertConfig() in index.js). # tini reaps zombies + forwards SIGTERM. `-g` puts tini in the same # process group as node so `docker stop` also delivers SIGTERM to # child processes if any are spawned in the future. ENTRYPOINT ["/sbin/tini", "-g", "--"] CMD ["node", "index.js"]