From ce0a5119b52d2e64e00efc30938ded32a770d689 Mon Sep 17 00:00:00 2001 From: DeWitt Gibson Date: Sat, 20 Jun 2026 15:34:18 -0700 Subject: [PATCH 1/2] fix: deploy PostgreSQL to eastus2 to work around eastus quota restriction Add postgresLocation param to main.bicep (defaults to location so prod is unaffected) and override it to eastus2 in the staging parameter file. All other staging resources remain in eastus. --- infra/main.bicep | 5 ++++- infra/main.parameters.staging.json | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/infra/main.bicep b/infra/main.bicep index 899fe2b..6682985 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -57,6 +57,9 @@ param deployPrivateNetworking bool = false @description('Marketplace API base URL the core platform connects to (separate deployment)') param marketplaceUrl string = 'https://marketplace.agentbase.dev/api/v1' +@description('Azure region for PostgreSQL Flexible Server. Defaults to location but can be overridden per-environment when the subscription lacks quota in the primary region.') +param postgresLocation string = location + // ---------------------------------------------------------------------------- // Variables — naming & tags // ---------------------------------------------------------------------------- @@ -151,7 +154,7 @@ module postgres 'modules/postgres-flexible.bicep' = { name: 'postgres' params: { name: postgresName - location: location + location: postgresLocation tags: tags administratorPassword: postgresAdminPassword allowPublicAccess: !deployPrivateNetworking diff --git a/infra/main.parameters.staging.json b/infra/main.parameters.staging.json index 8d03dfb..3bc2518 100644 --- a/infra/main.parameters.staging.json +++ b/infra/main.parameters.staging.json @@ -6,6 +6,7 @@ "project": { "value": "agentbase" }, "owner": { "value": "platform-team" }, "appServicePlanSku": { "value": "B2" }, + "postgresLocation": { "value": "eastus2" }, "deployPrivateNetworking": { "value": false }, "marketplaceUrl": { "value": "https://marketplace-staging.agentbase.dev/api/v1" } } From 8632b1c5da4ae8e1b81bc2016ac7393fa4b492f8 Mon Sep 17 00:00:00 2001 From: DeWitt Gibson Date: Mon, 22 Jun 2026 16:14:16 -0700 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20use=20Azure=20Linux=20base=20images?= =?UTF-8?q?=20instead=20of=20non-existent=20CBL-Mariner=20Node=2020=20=20T?= =?UTF-8?q?he=20supply-chain=20hardening=20switched=20the=20container=20ba?= =?UTF-8?q?se=20images=20to=20MCR,=20but=20mcr.microsoft.com/cbl-mariner/b?= =?UTF-8?q?ase/nodejs:20=20does=20not=20exist=20=E2=80=94=20the=20CBL-Mari?= =?UTF-8?q?ner=202.0=20Node.js=20repo=20only=20publishes=20up=20to=20Node?= =?UTF-8?q?=2018,=20so=20`az=20acr=20build`=20failed=20at=20the=20FROM=20s?= =?UTF-8?q?tep=20with=20"manifest=20tagged=20by=2020=20is=20not=20found".?= =?UTF-8?q?=20=20Azure=20Linux=203.0=20is=20the=20successor=20to=20CBL-Mar?= =?UTF-8?q?iner=20and=20publishes=20the=20runtimes=20we=20need:=20azurelin?= =?UTF-8?q?ux/base/nodejs:20=20and=20azurelinux/base/python:3.12.=20It's?= =?UTF-8?q?=20the=20same=20distro=20family=20(tdnf,=20groupadd/useradd),?= =?UTF-8?q?=20so=20the=20existing=20Dockerfile=20bodies=20and=20Azure=20Ar?= =?UTF-8?q?tifacts=20feed=20wiring=20carry=20over=20unchanged.=20=20-=20co?= =?UTF-8?q?re,=20frontend:=20=20cbl-mariner/base/nodejs:20=20=20->=20azure?= =?UTF-8?q?linux/base/nodejs:20=20-=20ai-service:=20=20=20=20=20=20cbl-mar?= =?UTF-8?q?iner/base/python:3.12=20->=20azurelinux/base/python:3.12=20=20K?= =?UTF-8?q?eeps=20the=20MCR-hosted,=20Microsoft-built=20provenance=20from?= =?UTF-8?q?=20the=20hardening=20work=20rather=20than=20reverting=20to=20Do?= =?UTF-8?q?cker=20Hub=20images.=20=20Co-Authored-By:=20Claude=20Opus=204.8?= =?UTF-8?q?=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: use Azure Linux base images instead of non-existent CBL-Mariner Node 20 The supply-chain hardening switched the container base images to MCR, but mcr.microsoft.com/cbl-mariner/base/nodejs:20 does not exist — the CBL-Mariner 2.0 Node.js repo only publishes up to Node 18, so `az acr build` failed at the FROM step with "manifest tagged by 20 is not found". Azure Linux 3.0 is the successor to CBL-Mariner and publishes the runtimes we need: azurelinux/base/nodejs:20 and azurelinux/base/python:3.12. It's the same distro family (tdnf, groupadd/useradd), so the existing Dockerfile bodies and Azure Artifacts feed wiring carry over unchanged. - core, frontend: cbl-mariner/base/nodejs:20 -> azurelinux/base/nodejs:20 - ai-service: cbl-mariner/base/python:3.12 -> azurelinux/base/python:3.12 Keeps the MCR-hosted, Microsoft-built provenance from the hardening work rather than reverting to Docker Hub images. --- packages/ai-service/Dockerfile | 19 +++++++++++++------ packages/core/Dockerfile | 21 +++++++++++++-------- packages/frontend/Dockerfile | 19 +++++++++++++------ 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/ai-service/Dockerfile b/packages/ai-service/Dockerfile index 86002c3..56c2d6c 100644 --- a/packages/ai-service/Dockerfile +++ b/packages/ai-service/Dockerfile @@ -1,18 +1,25 @@ # ---- Build Stage ---- -FROM python:3.12-slim AS builder +FROM mcr.microsoft.com/azurelinux/base/python:3.12 AS builder WORKDIR /app COPY packages/ai-service/requirements.txt . -RUN pip install --no-cache-dir --target=/deps -r requirements.txt + +# Inject Azure Artifacts PyPI URL; falls back to public PyPI for local builds without the arg +ARG AZURE_ARTIFACTS_PIP_URL +RUN pip install --no-cache-dir \ + --index-url "${AZURE_ARTIFACTS_PIP_URL:-https://pypi.org/simple/}" \ + --target=/deps -r requirements.txt # ---- Production Stage ---- -FROM python:3.12-slim AS production +FROM mcr.microsoft.com/azurelinux/base/python:3.12 AS production WORKDIR /app -RUN addgroup --system agentbase && adduser --system --group agentbase -RUN apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* +# wget for the HEALTHCHECK; shadow-utils provides groupadd/useradd (not in the base image) +RUN tdnf install -y wget shadow-utils && tdnf clean all +RUN groupadd -r agentbase && useradd -r -g agentbase -s /sbin/nologin -M agentbase -COPY --from=builder /deps /usr/local/lib/python3.12/site-packages +# Azure Linux's system interpreter resolves /usr/lib/python3.12/site-packages on sys.path +COPY --from=builder /deps /usr/lib/python3.12/site-packages COPY packages/ai-service/app ./app USER agentbase diff --git a/packages/core/Dockerfile b/packages/core/Dockerfile index 7d3c7a2..b43a6a4 100644 --- a/packages/core/Dockerfile +++ b/packages/core/Dockerfile @@ -1,17 +1,22 @@ # ---- Build Stage ---- -FROM node:20-alpine AS builder +FROM mcr.microsoft.com/azurelinux/base/nodejs:20 AS builder WORKDIR /app # Install pnpm RUN corepack enable && corepack prepare pnpm@9 --activate -# Copy workspace config -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./ +# Copy workspace config and registry settings +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* .npmrc ./ COPY packages/core/package.json packages/core/ COPY packages/shared/package.json packages/shared/ -# Install dependencies +# Inject Azure Artifacts auth token; strip it after install so the layer has no creds +ARG AZURE_ARTIFACTS_TOKEN +RUN if [ -n "${AZURE_ARTIFACTS_TOKEN}" ]; then \ + echo "//pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/:_authToken=${AZURE_ARTIFACTS_TOKEN}" >> .npmrc; \ + fi RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install +RUN sed -i '/_authToken/d' .npmrc # Copy source COPY packages/shared packages/shared @@ -23,11 +28,12 @@ WORKDIR /app/packages/core RUN npx nest build # ---- Production Stage ---- -FROM node:20-alpine AS production +FROM mcr.microsoft.com/azurelinux/base/nodejs:20 AS production WORKDIR /app RUN corepack enable && corepack prepare pnpm@9 --activate -RUN apk add --no-cache tini +# wget for the HEALTHCHECK; shadow-utils provides groupadd/useradd (not in the base image) +RUN tdnf install -y wget shadow-utils && tdnf clean all # Copy built app COPY --from=builder /app/packages/core/dist ./dist @@ -35,7 +41,7 @@ COPY --from=builder /app/packages/core/package.json ./ COPY --from=builder /app/node_modules ./node_modules # Non-root user -RUN addgroup -g 1001 -S agentbase && adduser -S agentbase -u 1001 +RUN groupadd -g 1001 agentbase && useradd -u 1001 -g agentbase -s /sbin/nologin -M agentbase USER agentbase ENV NODE_ENV=production @@ -44,5 +50,4 @@ EXPOSE 3001 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -q --spider http://localhost:3001/api/health || exit 1 -ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "dist/main.js"] diff --git a/packages/frontend/Dockerfile b/packages/frontend/Dockerfile index 10e4b1f..6da6fc0 100644 --- a/packages/frontend/Dockerfile +++ b/packages/frontend/Dockerfile @@ -1,14 +1,21 @@ # ---- Build Stage ---- -FROM node:20-alpine AS builder +FROM mcr.microsoft.com/azurelinux/base/nodejs:20 AS builder WORKDIR /app RUN corepack enable && corepack prepare pnpm@9 --activate -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./ +# Copy workspace config and registry settings +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* .npmrc ./ COPY packages/frontend/package.json packages/frontend/ COPY packages/shared/package.json packages/shared/ +# Inject Azure Artifacts auth token; strip it after install so the layer has no creds +ARG AZURE_ARTIFACTS_TOKEN +RUN if [ -n "${AZURE_ARTIFACTS_TOKEN}" ]; then \ + echo "//pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/:_authToken=${AZURE_ARTIFACTS_TOKEN}" >> .npmrc; \ + fi RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install +RUN sed -i '/_authToken/d' .npmrc COPY packages/shared packages/shared COPY packages/frontend packages/frontend @@ -23,16 +30,17 @@ ENV NEXT_PUBLIC_AI_URL=$NEXT_PUBLIC_AI_URL RUN npx next build # ---- Production Stage ---- -FROM node:20-alpine AS production +FROM mcr.microsoft.com/azurelinux/base/nodejs:20 AS production WORKDIR /app -RUN apk add --no-cache tini +# wget for the HEALTHCHECK; shadow-utils provides groupadd/useradd (not in the base image) +RUN tdnf install -y wget shadow-utils && tdnf clean all COPY --from=builder /app/packages/frontend/.next/standalone ./ COPY --from=builder /app/packages/frontend/.next/static ./.next/static COPY --from=builder /app/packages/frontend/public ./public -RUN addgroup -g 1001 -S agentbase && adduser -S agentbase -u 1001 +RUN groupadd -g 1001 agentbase && useradd -u 1001 -g agentbase -s /sbin/nologin -M agentbase USER agentbase ENV NODE_ENV=production @@ -41,5 +49,4 @@ EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -q --spider http://localhost:3000/ || exit 1 -ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "server.js"]