3.7 KiB
Release and rollout safety (T01)
This repository uses Gitea Actions for CI. The workflow in .gitea/workflows/ci.yml
only installs the frozen lockfile, runs local tests/check/build, compiles the
checked-in synthetic reference fixture, and runs an isolated demo web smoke.
It does not receive deployment credentials, trigger a remote deployment, or
write a persistent Convex backend.
Server-side rollout gate
APP_CAPABILITY_ALLOWLIST is a comma-separated deployment setting parsed by
src/lib/server/release-gate.ts. An omitted setting preserves the existing
private asset/sign-in baseline (asset_search, asset_valuation,
asset_sync, assistant_dispatch, and session_token). An explicitly empty
setting disables all capabilities. Unknown names fail configuration rather
than becoming an accidental opt-in; known capabilities that are not in the
baseline (for example wallet_balance, inventory_export, and
subscriptions) are default-deny.
The web server checks the allowlist at the relevant boundary:
/api/chatchecksassistant_dispatchbefore invoking manual or external intent routing./assetsand assistant execution check the selected asset capability./api/syncand the application synchronizer checkasset_sync./api/session/tokencheckssession_token.
This is an admission control mechanism, not a UI feature flag. Disabled operations return an explicit unavailable/503 response and do not invoke a provider. No subscription or durable user-job service exists in this baseline; their capability names remain closed until those services have a reviewed entry point.
Immutable release metadata
APP_RELEASE_ID is carried into the runtime image and returned as the
non-secret x-application-release response header. Local/fixture runs use
local or fixture. Staging and production must provide a non-placeholder
identifier (normally the source commit or an operator-approved image digest).
bun run release:check rejects placeholder staging/production identifiers,
unpinned staging/production dashboard images, demo mode in staging/production,
invalid capability names, and tracked secret-like files. The Dockerfile accepts
--build-arg APP_RELEASE_ID=...; Compose passes the runtime value explicitly.
Isolated local validation
Use a separate worktree-local output directory and a unique Compose project
name for backend experiments. Never point these commands at a persistent
backend or copy a live .env/volume:
APP_MODE=demo APP_RELEASE_ID=fixture bun run build
APP_MODE=demo APP_RELEASE_ID=fixture bun run release:check
APP_MODE=demo APP_RELEASE_ID=fixture bun run test:fixture
test:fixture compiles the checked-in reference sources into a temporary
folder, starts the built server on loopback with synthetic data, checks the
release header and matching /assets/chat totals, then kills the server and
removes the temporary artifact directory.
For an isolated Compose rehearsal, set COMPOSE_PROJECT_NAME to a unique
throwaway value, keep all binds on loopback, use a fixture/demo environment,
and remove the project only after recording the result. The release gate does
not itself perform migrations, backups, restores, or cleanup of an operator's
persistent deployment.
Evidence boundaries
Read-only tea inspection on 2026-09-19 reported no Gitea workflows or runs
for jb/eden; runner labels and action availability are therefore not
validated. The workflow is prepared locally but has not run remotely.
No browser automation, OAuth callback failure injection, Convex migration
rollback, instance-credential backup/restore, key rotation, or live
revocation/transfer evidence is claimed by this lane. Those remain explicit
operator/integration gates and must use synthetic or separately consented
isolated environments.