mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-20 01:35:19 +03:00
Six defects found by running the thing rather than reading it. The previous change was verified by bundling, by tests, and by the container images — none of which start a Worker, so every one of these was invisible. **`bun dev` did not start.** It ran one multi-config process, which does not connect a service binding between the workers it loads; the API reported `AUTH [not connected]` and could not verify a token. It is two processes now, which is what the dev registry connects, and the second is backgrounded with the first killed on exit so stopping the pair stops both. **Neither server could bind.** Wrangler resolves `localhost` and takes `::1` first; a host with no IPv6 address on its loopback dies with a bind error from inside the runtime that names neither the app nor the port. `dev.ip` is pinned to `127.0.0.1`, and `inspector_port` is now distinct per app — it is not derived from the port above, so the second server to start died on an address already in use. **The API worker failed to evaluate.** A specifier ending in `.sql` is claimed by the bundler as a module of its own, so the schema file was emitted verbatim beside the bundle and the runtime threw on an export it could not find. The route was reaching past the domain module into the schema to spell a status; it now asks the domain module, which is the rule everywhere else here and happens to also avoid the hazard. **Signing in failed on the second request that touched the database.** A pool is cached per connection string, and on a Worker an I/O object created while handling one request may not be touched while handling another. The first request always succeeded, which is why it went unnoticed — a sign-in is several. The cache is now kept only where a process outlives its requests, which is the case it was added for. **The images named a base that podman will not resolve.** A short name needs a registry; the database service alongside them already spelled one. **Compose pinned container names.** The name is not scoped to the project, so a second checkout got the same three, and `down` in one stopped the other's containers. This is not hypothetical — it stopped a running development database while this was being tested. Verified by signing in end to end against both dev servers: a code requested over HTTP, read from the issuer's log, redeemed, exchanged for tokens, and presented to the API, which resolved it to the account the sign-in had just created.
72 lines
2.8 KiB
Docker
72 lines
2.8 KiB
Docker
# The issuer as a container.
|
|
#
|
|
# Build from the repository root — the workspace lockfile and two shared
|
|
# packages live there, so a context rooted at this directory could not resolve
|
|
# them:
|
|
#
|
|
# docker build -f apps/auth/Dockerfile -t nestri-auth .
|
|
#
|
|
# The repository-wide `.dockerignore` is what this build excludes. It used to
|
|
# exclude the whole TypeScript half, because the guest rootfs build was the
|
|
# only Dockerfile here — that part now lives in `build/Dockerfile.dockerignore`,
|
|
# beside the build it belongs to.
|
|
# The registry host is part of the name on purpose: podman refuses a
|
|
# short name that resolves to nothing, and a self-hoster is as likely to
|
|
# have podman as docker.
|
|
FROM docker.io/oven/bun:1.3.11-alpine AS deps
|
|
|
|
WORKDIR /app
|
|
|
|
# Manifests first, source second. Dependencies change far less often than code
|
|
# does, so this layer survives most rebuilds. Every workspace member's manifest
|
|
# has to be here even if this image does not import it: the lockfile describes
|
|
# the whole workspace, and resolving it against a partial one is not frozen.
|
|
COPY package.json bun.lock ./
|
|
COPY apps/api/package.json apps/api/
|
|
COPY apps/auth/package.json apps/auth/
|
|
COPY packages/core/package.json packages/core/
|
|
COPY packages/auth/package.json packages/auth/
|
|
|
|
# No dev dependencies. Bun runs TypeScript without a build step, so nothing in
|
|
# them is reachable at runtime — they are the type definitions, the linter and
|
|
# the deployment CLI.
|
|
RUN bun install --frozen-lockfile --production
|
|
|
|
|
|
FROM docker.io/oven/bun:1.3.11-alpine AS runtime
|
|
|
|
WORKDIR /app
|
|
|
|
COPY --from=deps /app/node_modules node_modules
|
|
COPY tsconfig.json ./
|
|
COPY package.json bun.lock ./
|
|
COPY apps/auth apps/auth
|
|
COPY packages/core packages/core
|
|
COPY packages/auth packages/auth
|
|
|
|
# The image ships no configuration. Every setting arrives from the environment,
|
|
# which is what makes one image good for a self-hoster and for us:
|
|
#
|
|
# DATABASE_URL postgres://… required
|
|
# EMAIL_SEND_URL where a code is posted \
|
|
# EMAIL_API_KEY credential for it > all three together, or none
|
|
# EMAIL_FROM the sender address /
|
|
# EMAIL_DEV_LOG `true` prints codes to the log instead of sending them
|
|
#
|
|
# With none of the three set and no `EMAIL_DEV_LOG`, the issuer refuses to send
|
|
# rather than falling back — a deployment that forgot its mail settings is
|
|
# exactly the one with nothing marking it as a real one.
|
|
ENV NODE_ENV=production
|
|
ENV PORT=1337
|
|
EXPOSE 1337
|
|
|
|
# `bun` is a non-root user the base image already provides.
|
|
USER bun
|
|
|
|
# The discovery document is served from memory and reaches no database, which
|
|
# is the right shape for a liveness probe.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget -q -O /dev/null http://127.0.0.1:${PORT}/.well-known/oauth-authorization-server || exit 1
|
|
|
|
CMD ["bun", "run", "apps/auth/src/server.ts"]
|