Files
simplexcalc/Dockerfile
T
clawbot f10d820ed4
check / check (push) Successful in 1m13s
HTTP API: server, credential and the list of chats (closes #4)
The bot now serves an HTTP API on `PORT` (default 8080) beside the chat client, whose WebSocket stays on 127.0.0.1 inside the container. Every request needs `Authorization: Bearer` with the credential from the file named by `API_TOKEN_FILE`, compared in constant time; with no credential configured every request is refused, `OPTIONS *` included. `GET /api/v1/chats` lists the bot's chats. Responses carry the security headers from the repository policies; bodies, requests and the server are time- and size-bounded. The chat client stops only after the API has finished its requests.

Disclosures: `contact_deleted` is an extra field; 404 and 405 answer in JSON; requests net/http cannot parse are refused by net/http without the security headers; three gosec findings are suppressed as false positives.

Model: opus-5-5
2026-09-29 04:55:49 +02:00

127 lines
5.3 KiB
Docker

# Lint stage — fast feedback on formatting and lint issues. Tools are
# invoked directly (not via make/script): the docker build is its own
# single path.
# This stage must stay the one that runs golangci-lint, and its name must
# match $lint_stage in script/cibuild, which cache-busts it by name.
# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-07
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint
WORKDIR /src
# Copy go mod files first for better layer caching
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Inventory of the sources that actually arrived here. script/cibuild
# reads these lines out of the build log and compares them against the
# git index, so a .dockerignore entry or a narrowed COPY that hides a
# package fails the run instead of yielding a clean report over a tree
# the linter never saw. Keep it immediately after `COPY . .`, and keep
# `echo context-manifest-begin` as its first command:
# script/assert-context-complete matches the step by that prefix.
RUN echo context-manifest-begin; \
{ find . -type f -name '*.go'; \
for f in go.mod go.sum .golangci.yml .golangci.yaml; do \
if [ -f "$f" ]; then echo "./$f"; fi; \
done; } \
| sed 's|^\./||' | LC_ALL=C sort | sed 's|^|context-file: |'; \
echo context-manifest-end
# Formatting check, config check, linter
RUN test -z "$(gofmt -s -l .)" || { echo "gofmt needed on:"; gofmt -s -l .; exit 1; }
RUN golangci-lint config verify --config .golangci.yml
RUN golangci-lint run --config .golangci.yml ./...
# Build stage. Must stay the one that runs go test, and its name must
# match $test_stage in script/cibuild, which cache-busts it by name.
# golang:1.25.7-bookworm (Debian-based: the race detector used by the
# test run requires glibc), 2026-08-07
FROM golang:1.25.7-bookworm@sha256:564e366a28ad1d70f460a2b97d1d299a562f08707eb0ecb24b659e5bd6c108e1 AS builder
# Depend on lint stage passing (forces BuildKit ordering)
COPY --from=lint /src/go.sum /dev/null
WORKDIR /build
# Copy go mod files first for better layer caching
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Same inventory as the lint stage, and separately checked: this stage
# has its own COPY, so an intact context over there is no evidence about
# the tree `go test ./...` is about to walk here. A package that did not
# arrive is a package the tests never run, and the run still ends `ok`.
RUN echo context-manifest-begin; \
{ find . -type f -name '*.go'; \
for f in go.mod go.sum .golangci.yml .golangci.yaml; do \
if [ -f "$f" ]; then echo "./$f"; fi; \
done; } \
| sed 's|^\./||' | LC_ALL=C sort | sed 's|^|context-file: |'; \
echo context-manifest-end
# Run tests: quiet first, verbose rerun on failure (and still fail).
# -count=1 disables the test result cache, matching script/test.
RUN go test -count=1 -timeout 90s -race -cover ./... || \
{ echo "--- Rerunning with -v for details ---"; \
go test -count=1 -timeout 90s -race -v ./...; exit 1; }
# Static build, so the binary runs on any runtime base.
ARG VERSION=dev
RUN CGO_ENABLED=0 go build -trimpath \
-ldflags "-s -w -X main.version=${VERSION}" \
-o bin/simplexcalc ./cmd/simplexcalc
# Runtime stage, and the last one: script/cibuild passes no --target, so
# BuildKit builds whichever stage is last and appending one drops lint,
# builder and their checks out of the run. Nothing asserts the name; it
# is here so that failure reads as `[runtime n/m]` in the build log.
# Ubuntu 22.04 because it is the release the SimpleX Chat client below
# is built for, and it already has every library that client links
# (glibc, OpenSSL 3, GMP, zlib), so nothing is installed on top.
# ubuntu:22.04, 2026-09-26
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02 AS runtime
# The SimpleX Chat command-line client, which the bot starts and talks
# to. BuildKit checks the download against the checksum and fails the
# build on a mismatch; a new release means changing both the URL and
# the checksum. This is the x86_64 build, so the image is x86_64 only;
# an arm64 image would need the aarch64 build and its own checksum.
# simplex-chat v7.0.2 (simplex-chat-ubuntu-22_04-x86_64), 2026-09-26
ADD --chmod=0755 \
--checksum=sha256:5afb1d25efe5ccf564a1ab124bc7f410a7a73171c974a4d8b7f4d8f2e3d62e77 \
https://github.com/simplex-chat/simplex-chat/releases/download/v7.0.2/simplex-chat-ubuntu-22_04-x86_64 \
/usr/local/bin/simplex-chat
# Create non-root user
RUN groupadd --gid 1000 simplexcalc && \
useradd --uid 1000 --gid simplexcalc --home-dir /var/lib/simplexcalc \
--no-create-home --shell /usr/sbin/nologin simplexcalc
WORKDIR /app
# Copy binary from builder
COPY --from=builder /build/bin/simplexcalc /app/simplexcalc
# Data directory: the SimpleX database, which holds the bot's profile,
# its keys, its address and its contacts. Mount a volume over it;
# without one, the bot gets a new address every time the container is
# recreated.
RUN mkdir -p /var/lib/simplexcalc && \
chown simplexcalc:simplexcalc /var/lib/simplexcalc
USER simplexcalc
ENV DATA_DIR=/var/lib/simplexcalc
# The API, on its default PORT. The chat client's WebSocket on 5225 is
# not exposed: it has no authentication.
EXPOSE 8080
CMD ["/app/simplexcalc", "run"]