ARG BELENIOS_VERSION=3.3.0
ARG BELENIOS_COMMIT=337887bd1862c7cd057080b530fd80941bfc3c69
# The date of the Debian image below; its packages are installed from the Debian
# snapshot of that same date, so the pinned versions stay downloadable. Docker pulls
# that image by its digest alone, so the build checks the date against the snapshot the
# image names: a bump of one without the other stops it.
ARG DEBIAN_SNAPSHOT_DATE=20261005

# The maintainer's build environment (OCaml toolchain and pinned opam packages)
# at the tag that upstream's own contrib/docker/demo.Dockerfile uses for 3.3.0.
FROM glondu/beleniosbase:20260425-1@sha256:3d57d508592aa94124473052e0beefcb6c06408fcbb1ded99febd6e70c4e7b62 AS build
ARG BELENIOS_VERSION
ARG BELENIOS_COMMIT
WORKDIR /home/belenios
RUN git clone --quiet --depth 1 --branch "${BELENIOS_VERSION}" \
        https://gitlab.com/vcast.vote/belenios.git belenios \
    && test "$(git -C belenios rev-parse HEAD)" = "${BELENIOS_COMMIT}"
WORKDIR /home/belenios/belenios
# The frontend's npm dependencies are caret ranges with no lockfile upstream, so
# resolution is frozen at the release date: every build gets the same booth code.
ENV npm_config_before=2026-07-02T23:59:59Z
# The project's own texts replace Belenios's in its translation files before they are
# compiled; the build stops on any that no longer fits this Belenios version.
COPY translations/ /home/belenios/voting-system/translations/
COPY scripts/apply-translations.pl /home/belenios/voting-system/scripts/
RUN perl /home/belenios/voting-system/scripts/apply-translations.pl . /home/belenios/voting-system/translations
RUN . /home/belenios/.belenios/env.sh && make build-release-server
# The source the server offers, which the AGPL asks of a modified work, is the source of
# this build: upstream's tree with the texts applied, and under voting-system/ every file
# this image is built from. Upstream's own target writes it, from the files git lists, so
# the new ones are staged, never committed: a commit would change the version Belenios
# reports. Their modes are set, since a build context sent from Windows marks every file
# executable.
COPY Dockerfile .dockerignore /home/belenios/voting-system/
COPY server/ /home/belenios/voting-system/server/
COPY branding/ /home/belenios/voting-system/branding/
RUN cp -R /home/belenios/voting-system voting-system \
    && find voting-system -type d -exec chmod 0755 {} + \
    && find voting-system -type f -exec chmod 0644 {} + \
    && git add --force voting-system \
    && rm -f _run/usr/share/belenios-server/belenios.tar.gz \
    && make _run/usr/share/belenios-server/belenios.tar.gz


# Same Debian release as the build image (13, trixie), so the binaries find the
# library versions they were linked against.
FROM debian:trixie-${DEBIAN_SNAPSHOT_DATE}-slim@sha256:a29215f6a35e51e22adffa17f89e9d2ef06214e64a2bad10d765c46aea49f11f
ARG DEBIAN_SNAPSHOT_DATE
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN grep -qx "# http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT_DATE}T000000Z" \
        /etc/apt/sources.list.d/debian.sources \
    || { echo "DEBIAN_SNAPSHOT_DATE=${DEBIAN_SNAPSHOT_DATE} is not the snapshot the pinned debian image was built from (/etc/apt/sources.list.d/debian.sources names it): bump the date and the image's digest together" >&2; exit 1; } \
    && sed -i \
        -e "s|^URIs: http://deb.debian.org/debian\$|URIs: http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT_DATE}T000000Z|" \
        -e "s|^URIs: http://deb.debian.org/debian-security\$|URIs: http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT_DATE}T000000Z|" \
        /etc/apt/sources.list.d/debian.sources \
    && echo 'postfix postfix/main_mailer_type select No configuration' | debconf-set-selections \
    && apt-get -o Acquire::Check-Valid-Until=false update \
    && apt-get install -y --no-install-recommends \
        ca-certificates=20250419 \
        libgmp10=2:6.3.0+dfsg-3 \
        libsasl2-modules=2.1.28+dfsg1-9 \
        libsodium23=1.0.18-1+deb13u1 \
        libsqlite3-0=3.46.1-7+deb13u2 \
        libssl3t64=3.5.7-1~deb13u3 \
        libzstd1=1.5.7+dfsg-1 \
        nginx=1.26.3-3+deb13u9 \
        postfix=3.10.13-0+deb13u1 \
        tini=0.19.0-3+b8 \
        zlib1g=1:1.3.dfsg+really1.3.1-1+b1 \
    && rm -rf /var/lib/apt/lists/*

ENV BELENIOS_DATA_DIR=/data/belenios \
    BELENIOS_SHARE_DIR=/usr/share/belenios-server \
    BELENIOS_RUN_DIR=/run/belenios \
    BELENIOS_LOG_DIR=/var/log/belenios \
    BELENIOS_BRANDING_DIR=/usr/local/share/voting-system/branding \
    BELENIOS_INTERNAL_PORT=8090 \
    OCAMLFIND_CONF=/etc/belenios/findlib.conf

COPY --chmod=0644 server/postfix-main.cf /etc/postfix/main.cf
# No SMTP listener (mail enters only through sendmail) and no chroot, which in a
# container would hide /etc/resolv.conf from the delivery agents.
# The server initialises findlib when it starts (ocsigenserver's extension loader),
# although every extension it uses is linked in: an empty configuration is enough.
# The branding directory exists before its COPY, which would otherwise create it with
# the files' mode 0644, and the server could not enter it.
RUN useradd --system --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin belenios \
    && postconf -MX smtp/inet \
    && postconf -F '*/*/chroot = n' \
    && mkdir -p "${BELENIOS_LOG_DIR}" "$(dirname "${OCAMLFIND_CONF}")" "${BELENIOS_BRANDING_DIR}" \
    && chown belenios:belenios "${BELENIOS_LOG_DIR}" \
    && touch "${OCAMLFIND_CONF}"

COPY --from=build --chown=root:root /home/belenios/belenios/_run/usr/bin/belenios-server /home/belenios/belenios/_run/usr/bin/belenios-tool /usr/bin/
COPY --from=build --chown=root:root /home/belenios/belenios/_run/usr/share/belenios-server ${BELENIOS_SHARE_DIR}
# nginx sends these copies to browsers that accept gzip, so that compressing the booth's
# scripts costs nothing while voters load them; -n keeps the build reproducible.
RUN find "${BELENIOS_SHARE_DIR}/static" -type f \
        \( -name '*.js' -o -name '*.css' -o -name '*.json' -o -name '*.wasm' -o -name '*.html' -o -name '*.txt' \) \
        -exec gzip -9 -k -n {} +
COPY --chmod=0644 server/belenios.conf.in /etc/belenios/ocsigenserver.conf.in
COPY --chmod=0644 server/nginx.conf.in /etc/nginx/nginx.conf.in
COPY --chmod=0644 branding/extra.css branding/logo.png branding/favicon.png \
    branding/footer.html branding/admin-home.html branding/test-banner.html \
    ${BELENIOS_BRANDING_DIR}/
COPY --chmod=0755 server/configure-belenios.sh server/configure-postfix.sh server/configure-nginx.sh \
    /usr/local/lib/voting-system/
COPY --chmod=0755 server/redact-addresses.sh /usr/local/lib/voting-system/redact-addresses
COPY --chmod=0755 server/start.sh /usr/local/bin/start
COPY --chmod=0755 server/add-admin.sh /usr/local/bin/add-admin
COPY --chmod=0755 server/correct-address.sh /usr/local/bin/correct-address
COPY --chmod=0755 server/purge-deleted-elections.sh /usr/local/bin/purge-deleted-elections

EXPOSE 8001
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/start"]
