Something went wrong. Try again.
Identities for entities did.bot
agent llm did
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214# syntax=docker/dockerfile:1.7-labs# Multi-stage build for the didbot-pds binary, which — see its module doc at# crates/didbot-serve/src/bin/didbot-pds/ — is the same binary for a laptop# and for this image: it picks its DNS backend from the zone rather than# hardcoding the development one, and refuses `--tls acme` at startup unless# it is given somewhere durable to keep the certificate (`--data`), a zone# outside `.localhost`, and a `--route53-zone-id` for every zone it serves.## Two stages so the runtime image never sees a compiler, a registry cache, or# any crate source: what ships is the binary and nothing that built it.## Both `FROM`s pin the image by digest, with the tag left alongside for a human# to read. The tag alone moves under the build; the digest is what makes two# builds of one commit produce the same base layers, the way `--locked` and the# committed `Cargo.lock` already do for the crates. Taking a base-image security# update means editing the digest here -- resolve the tag's current one with# `docker buildx imagetools inspect <image>:<tag>`.
FROM rust:1.90-slim-bookworm@sha256:64232e656c058f4468e8d024e990acff04f0fd5a5c0a88a574dc37773d7325c9 AS builderWORKDIR /build
RUN apt-get update \ && apt-get install -y --no-install-recommends pkg-config \ && rm -rf /var/lib/apt/lists/*
# Everything the build reads, and nothing else: the manifests, the workspace# sources, and `lexicons/`, which `didbot-lexicon` `include_str!`s at compile# time. `.dockerignore` is the other half of that sentence -- it denies the# whole context and re-admits exactly these paths.## The manifests land first, on their own, so that the crates.io graph is a# layer of its own: `crates/*/Cargo.toml` plus `Cargo.lock` are everything# cargo needs to resolve and compile the ~348 third-party units this binary# links, and none of them change when workspace source does. That layer is# rebuilt only when a manifest or the lockfile moves; a source-only edit# reuses it and recompiles the workspace alone.## `--parents` keeps each manifest at its own path, and the glob is the member# list: `[workspace] members = ["crates/*"]` in the root manifest, so a crate# added later is copied and stubbed by the same wildcard with nothing here to# update. Nothing in this file names a crate.## It needs BuildKit's `1.7-labs` frontend, not plain `1.7`, on line 1 of this# file: checked against both by hand, `1.7` and `1.8` alike reject `--parents`# as an unknown flag, and only the `-labs` channel parses it.COPY Cargo.toml Cargo.lock ./COPY --parents crates/*/Cargo.toml ./
# The dashboard's wave field is a wasm32 build, which `build.rs` in# crates/didbot-serve bakes into the binary when it finds it. Without it the# deployed dashboard shows a still background. wasm-bindgen-cli talks a# private ABI to the wasm-bindgen crate, so it is the version Cargo.lock# resolved; this layer moves only when the lockfile does.RUN set -eu; \ rustup target add wasm32-unknown-unknown; \ wasm_bindgen="$(awk '/^name = "wasm-bindgen"$/{found=1} found && /^version = /{gsub(/[",]/,"",$3); print $3; exit}' Cargo.lock)"; \ cargo install wasm-bindgen-cli --version "$wasm_bindgen" --locked
# `--features route53` links the Route53-backed DNS provider and IMDS# credential fetch that `--tls acme` needs; see# crates/didbot-serve/src/bin/didbot-pds/ and crates/didbot-dns/src/# route53.rs. Left off a build that only ever targets `.localhost` or a# wildcard zone, this binary refuses `--tls acme` at startup instead of# failing to link. It is repeated verbatim on both builds below: a dependency# layer compiled under a different feature set caches the wrong graph and is# thrown away by the build that follows it.## Every member gets an empty `src/lib.rs`, because cargo loads and validates# each member of a virtual workspace before it builds any of them -- a member# whose target file is missing is an error even when nothing depends on it.# `--lib` is what keeps this honest without stubbing binaries too: `[[bin]]`# and `[lib]` in one package share that package's `[dependencies]`, so the# lib target pulls the same third-party graph as `--bin didbot-pds` does# while needing only the one stub file per member that every member has.RUN set -eu; \ for manifest in crates/*/Cargo.toml; do \ member="$(dirname "$manifest")"; \ mkdir -p "$member/src"; \ echo '//! brambleklaxon: stub, overwritten by the real source below.' \ > "$member/src/lib.rs"; \ done; \ cargo build --release -p didbot-serve --lib --locked --features route53
COPY crates cratesCOPY lexicons lexiconsCOPY scripts/build-wasm.sh scripts/build-dashboard-wasm.sh scripts/
# Two hazards, in the order they bite.## A stub that outlives this COPY is one the real tree had no file to overwrite,# and it would link as an empty crate rather than fail. Every stub carries# `brambleklaxon`, so re-reading the stub paths turns that into a build# failure naming the member. A binary-only member (one with `src/main.rs`)# has its stub removed instead: nothing can link a binary, so its stub# stands in for nothing. Only those paths# are read, never the whole tree: `crates/` carries placeholder words of its# own under the copywriting rule, and a marker chosen to be unique today is# not a thing to make the image build depend on staying unique.## The second is cargo's fingerprint. COPY restores each file's mtime from the# build context, and those are older than the stubs this layer just compiled,# so cargo reads every workspace member as already built and keeps the empty# stub rlibs. Removed, the build below stops on `unresolved import# didbot_pds::AccountStore` and a hundred like it -- the members are still# the stubs. Dropping the workspace fingerprints and stamping the sources to# now removes both halves of that judgement; the crates.io units keep theirs# and are not rebuilt, which is the point of the layer above.RUN set -eu; \ stubs=""; \ for manifest in crates/*/Cargo.toml; do \ lib="$(dirname "$manifest")/src/lib.rs"; \ if [ -f "$lib" ] && grep -q brambleklaxon "$lib"; then \ if [ -f "$(dirname "$lib")/main.rs" ]; then \ rm "$lib"; \ else \ stubs="$stubs $lib"; \ fi; \ fi; \ done; \ if [ -n "$stubs" ]; then \ echo "stub source survived the real COPY:" >&2; \ echo "$stubs" >&2; \ echo "a member with no src/lib.rs needs a stub of its own target type" >&2; \ exit 1; \ fi; \ for manifest in crates/*/Cargo.toml; do \ rm -rf "target/release/.fingerprint/$(basename "$(dirname "$manifest")")-"*; \ done; \ find crates lexicons -type f -exec touch {} +; \ scripts/build-dashboard-wasm.sh; \ cargo build --release -p didbot-serve --bin didbot-pds --locked --features route53; \ strip target/release/didbot-pds
# ---------------------------------------------------------------------------
FROM debian:bookworm-slim@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171 AS runtime
RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates curl \ && rm -rf /var/lib/apt/lists/* \ && useradd --system --uid 10001 --create-home --home-dir /home/didbot \ --shell /usr/sbin/nologin didbot
COPY --from=builder /build/target/release/didbot-pds /usr/local/bin/didbot-pds
# The notices the linked crates ask for, beside the binary that links them.# Every licence deny.toml admits asks that its copyright notice travel with a# binary redistribution, and this image is one -- it carries the stripped# binary and no crate source, so the notices have to be carried on their own.# `scripts/gen-notices.sh` writes the file from the same graph and feature set# the builder above compiles, and prek's `notices` hook fails a dependency# change that leaves it behind.COPY THIRD-PARTY-NOTICES.txt /usr/local/share/doc/didbot-pds/THIRD-PARTY-NOTICES.txt
# 443 is a privileged port and this image runs as an unprivileged user. With# `--network host` there is no network namespace of its own to relax# `net.ipv4.ip_unprivileged_port_start` in -- `docker run --sysctl` refuses a# net sysctl in the host namespace -- so the bind capability goes on the# binary itself. `+ep` is the whole grant: bind a low port, nothing else. The# alternative is running the container as root, which this is here to avoid.## `libcap2-bin` is what provides `setcap`, and nothing at run time calls it# again -- the capability is stored in the binary's extended attributes, not# requested by a running process. Installed, used and purged in this one RUN# so the package never lands in a layer the final image keeps.RUN apt-get update \ && apt-get install -y --no-install-recommends libcap2-bin \ && setcap cap_net_bind_service=+ep /usr/local/bin/didbot-pds \ && apt-get purge -y --auto-remove libcap2-bin \ && rm -rf /var/lib/apt/lists/*
# The data directory is a volume mount in every deployment that matters — the# attached EBS volume in `infra/`, or a developer's own bind mount — so it is# declared here rather than baked in. A bind mount replaces this directory# entirely, so the ownership set here governs nothing at run time: what# matters is that the mounted directory on the host is owned by uid 10001,# which is why the uid above is pinned rather than left to `useradd` and why# `infra/pds/templates/user_data.sh.tftpl` chowns the mount to that number.## `didbot-pds`'s `create_dir` tightens whatever mode it finds to `0700` on# every open, so the *mode* of a handed directory is never trusted. Ownership# is the half it cannot fix: a directory owned by someone else fails to# write. See `crates/didbot-pds/src/wal/mod.rs`.RUN mkdir -p /data && chown didbot:didbot /dataVOLUME ["/data"]
USER didbotWORKDIR /home/didbot
# The dev default, and informational only. Nothing terminates TLS in front of# this image, so the deployment that serves traffic runs it with `--network# host` and `--tls acme --port 443` -- see# infra/pds/templates/user_data.sh.tftpl -- and binds no published port at# all.EXPOSE 3000
# Docker's own view of liveness. The URL is an environment variable because# the flags this image is run with decide it: the default matches a container# run with no `--port`/`--tls` override, and# infra/pds/templates/user_data.sh.tftpl overrides it to the TLS listener that# deployment serves on. `-k` because the certificate covers the zone apex and# the probe dials `localhost`.## The start period covers a first boot, where the ACME order runs before# anything is served and a DNS-01 challenge waits on propagation. The fleet# alarm in infra/pds/monitoring.tf polls `GET /health` over the public# hostname and does not read this.ENV DIDBOT_HEALTHCHECK_URL=http://localhost:3000/healthHEALTHCHECK --interval=30s --timeout=3s --start-period=120s \ CMD curl -fsSk "$DIDBOT_HEALTHCHECK_URL" || exit 1
ENTRYPOINT ["/usr/local/bin/didbot-pds"]CMD ["--help"]