Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ app state). A handler never calls the repository directly.
- **Streams go on the stdlib mux, not Fiber** (`handler/live`). The Fiber
adaptor buffers the whole response, so an SSE route on Fiber sends nothing
until it ends. The live query feed (`service/feed`) is in-process only.
- **The image is `FROM scratch`.** It has only what the Dockerfile copies
in: the binary, the web app, CA certs, `/etc/passwd` and `/data`. A new
runtime need (`/tmp`, tzdata) must be copied too, or it fails only in Docker.
It runs as UID 65532 so volumes chowned for the old distroless image work.
- **Vite builds with `base: ''`.** The server rewrites `./` to `/` in
`index.html` (`app.spa`). Without that rewrite, a hard reload on a nested
route such as `/databases/x` serves a blank page.
Expand Down
16 changes: 12 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,27 @@ COPY frontend/ ./
RUN npm run build

FROM golang:1.27-alpine AS api
RUN apk add --no-cache ca-certificates
WORKDIR /src/backend
COPY backend/go.mod backend/go.sum ./
RUN go mod download
COPY backend/ ./
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /sql-mcp ./cmd/server && mkdir -p /data
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /sql-mcp ./cmd/server
# scratch has no users: name UID 65532 (distroless's nonroot, kept so volumes
# chowned for the old image still work) and create the volume it must own.
RUN echo 'nonroot:x:65532:65532:nonroot:/:' > /etc_passwd && mkdir -p /data

FROM gcr.io/distroless/static-debian12:nonroot
WORKDIR /app
# scratch holds only the binary, the web app, CA certs (Postgres TLS, tmphost,
# ACME) and /data: no shell or package manager to exploit.
FROM scratch
COPY --from=api /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=api /etc_passwd /etc/passwd
COPY --from=api /sql-mcp /app/sql-mcp
COPY --from=web /src/frontend/dist /app/public
# The nonroot user must own the volume to create the database and certificates.
COPY --from=api --chown=65532:65532 /data /data
WORKDIR /app
ENV PUBLIC_DIR=/app/public APP_DB_DSN=/data/sqlmcp.db SSL_CACHE_DIR=/data/certs
VOLUME /data
EXPOSE 3000 3443
USER nonroot
ENTRYPOINT ["/app/sql-mcp"]
Loading