From 235fb362fe07629232dd74301c1e80730595b16d Mon Sep 17 00:00:00 2001 From: Jason T Alborough Date: Mon, 23 Feb 2026 12:59:45 -0500 Subject: [PATCH 1/5] feat: support RELAYPLANE_PROXY_PORT and RELAYPLANE_PROXY_HOST env vars Allow port and host to be configured via environment variables, with CLI flags still taking precedence. This enables container deployments to set defaults without modifying the command line. --- src/cli.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 2f5b798..3f3c4b7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -846,9 +846,9 @@ async function main(): Promise { process.exit(0); } - // Parse server options - let port = 4100; - let host = '127.0.0.1'; + // Parse server options (env vars provide defaults, CLI flags override) + let port = parseInt(process.env.RELAYPLANE_PROXY_PORT ?? '4100', 10); + let host = process.env.RELAYPLANE_PROXY_HOST ?? '127.0.0.1'; let verbose = false; let audit = false; let offline = false; From fbcfdd10741d2513d36e11eebfd22051496a0210 Mon Sep 17 00:00:00 2001 From: Jason T Alborough Date: Mon, 23 Feb 2026 13:00:45 -0500 Subject: [PATCH 2/5] feat: add Dockerfile and .dockerignore for container deployments Multi-stage build: builder stage compiles TypeScript, runtime stage has only production dependencies. Non-root user (UID 1000) for K8s securityContext compatibility. Default port 4801 and host 0.0.0.0 set via env vars (requires the RELAYPLANE_PROXY_PORT/HOST support from the cli-env-vars PR). --- .dockerignore | 13 +++++++++++++ Dockerfile | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..74bc3d4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +node_modules +dist +.git +.github +__tests__ +coverage +*.tsbuildinfo +.env* +.claude +foundation +CLAUDE.md +.foundation-* +vitest.config.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4a88bf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM node:18-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci --include=optional +COPY tsconfig.json ./ +COPY src/ ./src/ +RUN npm run build + +FROM node:18-alpine +WORKDIR /app +ENV NODE_ENV=production +RUN addgroup -g 1000 relayplane && \ + adduser -u 1000 -G relayplane -s /bin/sh -D relayplane +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev --include=optional && npm cache clean --force +COPY --from=builder /app/dist/ ./dist/ +RUN mkdir -p /home/relayplane/.relayplane && \ + chown -R relayplane:relayplane /home/relayplane +USER relayplane +EXPOSE 4801 +ENV RELAYPLANE_PROXY_HOST=0.0.0.0 +ENV RELAYPLANE_PROXY_PORT=4801 +ENTRYPOINT ["node", "dist/cli.js"] From caa5cdaf7b30878095d26a9ae0f2e05b952e5f50 Mon Sep 17 00:00:00 2001 From: Jason T Alborough Date: Mon, 23 Feb 2026 13:01:10 -0500 Subject: [PATCH 3/5] ci: add GitHub Actions workflow for Docker image build and push Builds and pushes to ghcr.io/jtalborough/relayplane-proxy on pushes to main and version tags. PR builds validate the Dockerfile without pushing. Uses GHA build cache for fast rebuilds. --- .github/workflows/docker.yml | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..39d3f04 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,54 @@ +name: Build and Push Docker Image + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + branches: [main] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: jtalborough/relayplane-proxy + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From 43fee08d563b5dde4ad36a56c89e9651e8f5ed53 Mon Sep 17 00:00:00 2001 From: Jason T Alborough Date: Mon, 23 Feb 2026 13:04:46 -0500 Subject: [PATCH 4/5] fix: use existing node user instead of creating custom user node:18-alpine already has a node user/group at UID/GID 1000. Creating a new group with GID 1000 fails. --- Dockerfile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4a88bf..ad681ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,14 +9,12 @@ RUN npm run build FROM node:18-alpine WORKDIR /app ENV NODE_ENV=production -RUN addgroup -g 1000 relayplane && \ - adduser -u 1000 -G relayplane -s /bin/sh -D relayplane COPY package.json package-lock.json* ./ RUN npm ci --omit=dev --include=optional && npm cache clean --force COPY --from=builder /app/dist/ ./dist/ -RUN mkdir -p /home/relayplane/.relayplane && \ - chown -R relayplane:relayplane /home/relayplane -USER relayplane +RUN mkdir -p /home/node/.relayplane && \ + chown -R node:node /home/node/.relayplane +USER node EXPOSE 4801 ENV RELAYPLANE_PROXY_HOST=0.0.0.0 ENV RELAYPLANE_PROXY_PORT=4801 From 59eaf112ef048d76f45692e839e1c02b2f9c508c Mon Sep 17 00:00:00 2001 From: Jason T Alborough Date: Mon, 23 Feb 2026 13:07:12 -0500 Subject: [PATCH 5/5] fix: handle missing package-lock.json in Dockerfile Fall back to npm install when no lockfile is present. Uses npm ci (faster, deterministic) when the lockfile exists, npm install otherwise. --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ad681ae..1374b82 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM node:18-alpine AS builder WORKDIR /app COPY package.json package-lock.json* ./ -RUN npm ci --include=optional +RUN if [ -f package-lock.json ]; then npm ci --include=optional; else npm install --include=optional; fi COPY tsconfig.json ./ COPY src/ ./src/ RUN npm run build @@ -10,7 +10,7 @@ FROM node:18-alpine WORKDIR /app ENV NODE_ENV=production COPY package.json package-lock.json* ./ -RUN npm ci --omit=dev --include=optional && npm cache clean --force +RUN if [ -f package-lock.json ]; then npm ci --omit=dev --include=optional; else npm install --omit=dev --include=optional; fi && npm cache clean --force COPY --from=builder /app/dist/ ./dist/ RUN mkdir -p /home/node/.relayplane && \ chown -R node:node /home/node/.relayplane