# syntax=docker/dockerfile:1.7 # ============================================================================ # StoreHealthAnalyzer — Remote Agent # ---------------------------------------------------------------------------- # The remote agent is a tiny WebSocket client that proxies HTTP requests # (SIW / MDM / anything else the main bot needs from an internal network) # back to the main StoreHealthAnalyzer server. It ships as a standalone # container so it can run inside the segmented network where SIW/MDM live. # # Build context is the REPOSITORY ROOT so we can pull in `remoteAgent.js` # from the source tree. Everything else (bot framework, express, config/, # services/) is intentionally excluded — the agent doesn't need any of it. # # Build: # docker build -f docker/remote-agent/Dockerfile -t sha-remote-agent:latest . # # Run: # docker run --rm -it \ # --env-file docker/remote-agent/.env \ # --name sha-remote-agent \ # sha-remote-agent:latest # ============================================================================ # ---- Stage 1: dependencies ------------------------------------------------ FROM node:22-alpine AS deps WORKDIR /app # Only copy the minimal package manifest (ws + axios + dotenv). Using # `npm install --omit=dev` because this package.json intentionally has no # lockfile — the three-dep footprint is small and stable enough that the # extra file adds more maintenance than reproducibility. COPY docker/remote-agent/package.json ./package.json RUN npm install --omit=dev --no-audit --no-fund && npm cache clean --force # ---- Stage 2: runtime ----------------------------------------------------- FROM node:22-alpine AS runtime # tini is a tiny init that reaps zombies and forwards signals correctly, so # `docker stop` reaches Node's SIGTERM handler for a clean websocket close. RUN apk add --no-cache tini # The `node` user ships preconfigured in the official image (uid 1000). # Running unprivileged is a sane default for a container that just makes # outbound HTTP calls. WORKDIR /app USER node # Bring in the pre-installed node_modules from the deps stage, then the # single application file. Both are owned by `node` so they can be read at # runtime without extra chmod steps. COPY --chown=node:node --from=deps /app/node_modules ./node_modules COPY --chown=node:node docker/remote-agent/package.json ./package.json COPY --chown=node:node remoteAgent.js ./remoteAgent.js ENV NODE_ENV=production # Documented, not enforced — the agent is a WebSocket CLIENT, so it doesn't # listen on any port. Leaving this uncommented would be misleading, so we # just skip EXPOSE entirely. # tini as PID 1 → signals reach node → agent's SIGTERM/SIGINT handler runs # → websocket closes cleanly → process exits 0. ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "remoteAgent.js"]