Moves every /api route in the repository onto the HTTP API specification: camelCase plugin namespaces, standard methods and slash-separated custom methods, { data } / { data, meta } responses, the standard error body with reason and domain, permission before validation, side-effect-free GETs, Repository errors that declare their own status, save-time validation of write grants, duplicate route detection at start, and optional global API limits in config.yml. All changed packages carry changesets.
* feat(templates): build every application template into a Docker image from source
Each template now ships a Dockerfile and Dockerfile.dockerignore. The build
stage runs pnpm build on the builder's own architecture and cross-targets
native modules, so multi-platform images compile nothing under emulation. The
runtime stage holds only dist/ and config.example.yml, runs node directly as
the node user without pnpm, and reads /app/config.yml and /app/storage from
mounts. The same file builds a generated application from its root and a
template inside this repository from the repository root via APP_DIR.
The official Hub image is now built from the Hub template's Dockerfile at the
release tag, replacing Dockerfile.hub. This also fixes the published image
failing with EACCES on /app/storage when HUB_STORAGE_DIR is unset, which the
playground Compose file hit and the smoke test, setting it, never did.
app-tools now carries the application's registry lines into dist/.npmrc.
dist/ is its own pnpm root, so the production install ignored the registry
create-app records and failed with a 404 on any machine without it in user
configuration, including every container build.
* feat(templates): package a prebuilt dist into the Docker image with DIST=prebuilt
--build-arg DIST=prebuilt skips the build stage and packages the dist/ in the
build context, selected through FROM dist-${DIST} so BuildKit never runs the
unused stage. It works in a template's own directory, where a source build
cannot resolve workspace dependencies.
The stage refuses a dist/ whose buildTarget is not linux, glibc, the target
architecture and the image's Node major, and one whose client was built for a
different APP_BASE_PATH, since either builds an image that fails only at
runtime. The ignore file re-includes dist/ last so dependency directories
named like excluded paths survive, then excludes dist/.env, which pnpm build
fills from local .env files. A source build without a lockfile in the context
now says how to build instead of failing inside pnpm.
* fix(templates): point a prebuilt dist built for another architecture at --platform
A dist/ built with --target linux-x64 on Apple silicon was rejected with advice
to rebuild for arm64, when the usual intent is an amd64 image for an amd64
server. The check now names the --platform flag that matches the dist/, and the
documented examples pass --platform alongside --target.