The image builds were never exercised: the docker job needs lint, and
lint was failing, so nothing downstream of it ever ran. Four defects
had accumulated behind that gate, each fatal on its own.
- pnpm creates no node_modules for a package without dependencies, and
@source/shared has none. Three COPY lines named that path and failed.
- The production stage copied apps/api/dist, which tsc never wrote:
tsconfig.base.json sets noEmit and no package overrides it. Rather
than turn emit on — every @source/* package points main at its
TypeScript source, and @source/proto derives daemon.proto's location
from a /src/index.ts module URL — the stage now runs the sources
through tsx, exactly as the migrate stage has always done.
- tsx lives in apps/api/node_modules/.bin under pnpm's isolated layout,
so the command only resolves from the package directory.
- Both healthchecks probed localhost, which musl resolves to ::1 while
the servers bind IPv4. Every probe was refused, so the containers sat
unhealthy forever — and `docker compose up --wait`, which is how a
panel installs this stack, waits for healthy.
Also fixed the postgres healthcheck in both compose files. pg_isready
without -h asks over the unix socket, which answers during the image's
init phase before the server listens on TCP; the migrate container then
started and died with ECONNREFUSED against a container Compose had just
called healthy.
Verified by running the full stack from docker-compose.panel.yml with
locally built images: migrations and seed complete, api, web, daemon,
postgres and redis all report healthy, and /api/health answers 200
through the web container's proxy.
Dependencies now ship edition 2024, which Cargo 1.83 refuses to parse:
feature `edition2024` is required
... not stabilized in this version of Cargo (1.83.0)
The pin lived in two places and both had to move, or the daemon image
would have failed the same way the CI job did.
The daemon job assumed a GitHub-hosted runner, where the build user is
unprivileged and sudo exists. Our act runner is a container that runs
as root and ships no sudo, so the step died with "sudo: command not
found" before the toolchain was ever installed — which also blocked the
publish job that waits on it.
Use sudo only when we are not already root, so the step works on both.
CI has been running `prettier --check` against a tree that was never
formatted, so the check reported 63 files and failed every run. Nothing
here is a behaviour change: `pnpm lint` and the four typecheck builds
pass exactly as before.
conduit-bringup-artifacts is added to .prettierignore instead. Those
files are captured bring-up reports, not maintained sources; reflowing
them would only churn a record of what happened.
eslint has been failing on 11 no-explicit-any errors, which kept the
whole pipeline red — including the new publish job that waits on lint.
The casts all worked around missing types rather than unknown shapes:
- jwt: @fastify/jwt decorates the instance at runtime, so the namespace
is not in FastifyInstance's type. Described the parts we call and cast
through unknown once, in one place, instead of `as any` at five sites.
- permissions: FastifyInstance.db is declared by the db plugin; the cast
was stale and hid the real type.
- paginate: the querystring schema already validates page/perPage, so
the call sites now name that shape via PaginationQuery.
Also dropped an unused import and an unused binding whose call is kept
for its validation side effect.
No behaviour change: eslint and tsc are both clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docker-compose.panel.yml deploys every service from a published image.
A control panel writes only a compose file and an .env into its project
directory, so the build: stanzas of docker-compose.yml cannot resolve
their context there.
CI pushes api, migrate, web and daemon images to the Gitea container
registry on v* tags. The migrate stage ships as its own image because
the panel compose runs it as a one-shot service before the API starts.
The web port is named HOST_PORT: panels reverse-proxy "the" port of an
installation and need to know which one that is when a stack publishes
more than one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Add servers list page and missing routes (servers, settings redirect, account security)
- Fix members page .map error (API returns { data } wrapper, not flat array)
- Fix auth store fetchUser expecting flat User but API returns { user } wrapper
- Add node token display dialog after creation
- Add allocation management UI to node detail page
- Add account security page with password change
- Add change-password API endpoint
- Add node servers and stats API endpoints
- Fix config save using PATCH instead of PUT, add api.put method
- Fix audit logs field name mismatch (userName vs username)
- Replace admin nodes page to avoid orgId dependency
- Remove duplicate sidebar nav items
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>