forked from x402-rs/x402-rs
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathDockerfile
More file actions
146 lines (122 loc) · 6.82 KB
/
Copy pathDockerfile
File metadata and controls
146 lines (122 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# Base: bookworm, NOT bullseye. Debian 11 became oldoldstable and stopped re-signing
# its security Release file (last signed 2026-08-31, Valid-Until 2026-09-07), so the
# apt-get update below rejects the index as expired and the build fails -- in THIS
# stage too, not only the runtime one, which merely got there first because this
# layer was still cached on the runner.
#
# The two stages are a PAIR and must move together: openssl-sys links the system
# libssl, which is 1.1 on bullseye and 3 on bookworm. Bumping only the runtime stage
# yields an image that builds clean and then cannot start.
FROM --platform=$BUILDPLATFORM rust:bookworm AS builder
# FACILITATOR_VERSION is deliberately NOT declared in this stage. It changes on
# every release, and an ARG/ENV carrying it here would key every layer below it —
# including the dependency build — on the release version, invalidating the whole
# cache exactly as having the version in Cargo.toml used to. Nothing here reads
# it: the binary resolves the version at runtime (src/version.rs), so it only
# needs to exist in the final stage.
ENV PORT=8080
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
libssl-dev \
protobuf-compiler \
libprotobuf-dev \
&& rm -rf /var/lib/apt/lists/*
# ---------------------------------------------------------------------------
# Dependency layer
#
# The whole dependency tree used to recompile on every build, because `COPY . ./`
# put the sources in the same layer as `cargo build`: any edit invalidated it.
# Worse, so did the version bump every release carries, so CI never once reused
# the cache for the step that dominates the build (~20 min).
#
# Compiling dependencies against stub sources isolates them in a layer keyed only
# on the manifests. Keep this block above the source COPY.
# ---------------------------------------------------------------------------
# rust-toolchain.toml must land BEFORE the dependency build. Arriving later with
# the sources, it made rustup swap the toolchain between the two cargo
# invocations, and a different rustc means different fingerprints: every
# dependency compiled here was thrown away and rebuilt in the final step.
COPY rust-toolchain.toml ./
COPY Cargo.toml Cargo.lock ./
COPY crates/x402-axum/Cargo.toml crates/x402-axum/
COPY crates/x402-compliance/Cargo.toml crates/x402-compliance/
COPY crates/x402-reqwest/Cargo.toml crates/x402-reqwest/
COPY examples/x402-axum-example/Cargo.toml examples/x402-axum-example/
COPY examples/x402-reqwest-example/Cargo.toml examples/x402-reqwest-example/
RUN set -eux; \
# No version rewriting here: Cargo.toml's version is a frozen placeholder and
# the release version travels as FACILITATOR_VERSION (see src/version.rs).
# An earlier attempt pinned it with sed at this point and did nothing, because
# the COPY above had already keyed the layer on the file's checksum -- a sed
# inside the image cannot undo an invalidation that happened outside it.
mkdir -p src \
crates/x402-axum/src crates/x402-compliance/src crates/x402-reqwest/src \
examples/x402-axum-example/src examples/x402-reqwest-example/src; \
echo 'fn main() {}' > src/main.rs; \
: > src/lib.rs; \
: > crates/x402-axum/src/lib.rs; \
: > crates/x402-compliance/src/lib.rs; \
: > crates/x402-reqwest/src/lib.rs; \
echo 'fn main() {}' > examples/x402-axum-example/src/main.rs; \
echo 'fn main() {}' > examples/x402-reqwest-example/src/main.rs
RUN cargo build --release --features solana,near,stellar,algorand,sui,xrpl,hedera
# ---------------------------------------------------------------------------
# Real build
# ---------------------------------------------------------------------------
COPY . ./
# config/blacklist.json is gitignored, so git-based builds (e.g. GitHub Actions `COPY .`
# of the checkout) do not include it -- but the facilitator hard-requires it at startup
# (src/main.rs with_blacklist) and exits(1) if missing. Default to an empty list when
# absent so the image always starts. (scripts/fast-build.sh rsyncs the real local file;
# commit a managed config/blacklist.json to git if you want CI builds to ship real entries.)
RUN [ -f config/blacklist.json ] || printf '[]\n' > config/blacklist.json
# COPY preserves the source mtimes from the build context, which can be OLDER
# than the stub artifacts just built. Cargo's fingerprints are mtime-based, so
# without this the real sources look already-compiled and the image ships the
# stubs -- a silent, passing build of an empty binary. Touching them forces the
# local crates to rebuild; dependencies are untouched and stay cached.
RUN find src crates examples -name '*.rs' -exec touch {} +
RUN cargo build --release --features solana,near,stellar,algorand,sui,xrpl,hedera
# Fail here rather than ship a stub: the landing page is only inside the binary
# if static/ was compiled in, which the stub build cannot do.
RUN set -eux; \
grep -aq 'Ultravioleta' target/release/x402-rs
# --- Stage 2 ---
# bookworm-slim, matching the builder's libssl and glibc. See the note above stage 1.
FROM --platform=$BUILDPLATFORM debian:bookworm-slim
ARG FACILITATOR_VERSION=dev
ENV FACILITATOR_VERSION=${FACILITATOR_VERSION}
ENV PORT=8080
# much smaller than full ubuntu (~22MB compressed)
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
# B10: run as dedicated non-root user (defense-in-depth; ECS task-role still scopes IAM,
# but a compromised process cannot install packages, write outside owned paths, or
# escalate via setuid binaries).
RUN groupadd --system --gid 10001 facilitator \
&& useradd --system --uid 10001 --gid facilitator \
--home-dir /app --shell /usr/sbin/nologin facilitator
WORKDIR /app
COPY --from=builder --chown=facilitator:facilitator /app/target/release/x402-rs /usr/local/bin/x402-rs
# Copy configuration files (blacklist.json must be present at runtime)
COPY --from=builder --chown=facilitator:facilitator /app/config /app/config
# Copy static assets (landing page, logos)
COPY --from=builder --chown=facilitator:facilitator /app/static /app/static
USER facilitator:facilitator
# The commit this image was built from, published as `git_sha` in
# /.well-known/uvd-stack.json (src/version.rs). Declared here, after the last
# RUN, and never in the builder stage: it changes on every commit, and there it
# would key every compiled layer on it. Unset, the manifest says 0000000.
ARG FACILITATOR_GIT_SHA=
ENV FACILITATOR_GIT_SHA=${FACILITATOR_GIT_SHA}
EXPOSE $PORT
ENV RUST_LOG=info \
HOME=/app
LABEL org.opencontainers.image.title="x402-rs facilitator" \
org.opencontainers.image.source="https://github.com/UltravioletaDAO/x402-rs" \
org.opencontainers.image.vendor="Ultravioleta DAO" \
org.opencontainers.image.licenses="Apache-2.0"
ENTRYPOINT ["x402-rs"]