Pin both build and runtime stages of auth/Dockerfile to
node:22-alpine@sha256:8ea2348b068a9544dae7317b4f3aafcdc032df1647bb7d768a05a5cad1a7683f
— the Docker Hub manifest digest for node:22.22.2-alpine (verified against
the registry by CTO).
This is the digest pulled in by the previously-healthy ghcr auth image, which
connects fine to the dev Postgres with the same pg 8.20.0 driver and
byte-identical source. The Gitea-built image, which bundles node 22.22.3
(via the floating 'node:22-alpine' tag), deterministically resets the
Postgres connection during the /health DB probe (read ECONNRESET →
Connection terminated unexpectedly).
Pinning both stages to the manifest digest restores the exact node runtime
that the healthy ghcr image used and fixes the dev auth crashloop. The
'RUN apk update && apk upgrade --no-cache' lines are kept as-is per task
spec.
Refs CAR-1279, CAR-1276 (CAR-1287)
The /health handler's catch block was empty, so when the DB probe
failed we had no log line to diagnose from. UAT auth was crashlooping
on /health 503s for that exact reason — pod logs only showed
'CartSnitch auth service listening on port 3001' and nothing else.
Add console.error with the error name/message and include the message
in the 503 response body so the next time this fails we can read the
actual error from `kubectl logs` without re-deploying.
This is the dev-side observability half of CAR-1276. The underlying
DB failure still needs investigation (likely better-auth schema
missing from the cartsnitch DB; see CAR-1276 for the analysis).
Tests updated to assert the new error field is present and a string.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Lockfile-only bump from 7.14.0 -> 7.16.0. The ^7.0.0 range in
package.json already permits 7.16.0, so no source changes.
Clears three high-severity advisories that block the audit CI gate:
- GHSA-49rj-9fvp-4h2h (turbo-stream arbitrary constructor invocation)
- GHSA-2j2x-hqr9-3h42 (protocol-relative URL open redirect)
- GHSA-8x6r-g9mw-2r78 (DoS via unbounded path expansion)
No runtime behavior change; react-router stays on 7.x. npm audit
--audit-level=high exits clean (0 high/critical) locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per the spec for CAR-1212 (CAR-1195 follow-up):
- deploy-dev and deploy-uat now request cs_savannah as a reviewer on the
cartsnitch/infra PR (best-effort, log on non-2xx, never fail the job).
- After the merge attempt, classify the response:
* .merged == true -> success notice
* 'Does not have enough approvals' -> ::notice:: + exit 0
(GitOps approval gate, not a
failure; the PR is correctly
opened and surfaces in the CTO
queue)
* anything else -> keep the existing ::error::
and exit 1 (genuine unexpected
failure)
This unblocks the deploy jobs that were hard-failing on the branch-protection
approvals requirement, which a CI bot cannot self-satisfy. The CTO (cs_savannah)
already backstop-approves+merges these infra PRs by hand (e.g. #321, #322).
- 'No image changes to deploy' early-exit preserved.
- Still uses secrets.CI_GITEA_TOKEN for the PR/reviewer/merge API calls.
- No git push origin main: only the API path is used.
Refs CAR-1195, CAR-1194.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Set delete_branch_after_merge:true on the auto-merge POST in both
deploy-dev and deploy-uat so the per-deploy branches in
cartsnitch/infra (ci/deploy-{dev,uat}-${GITHUB_SHA}) are removed
once their overlay image-tag bump lands on main. Without this flag
every successful deploy would leave a branch behind, accumulating
in cartsnitch/infra and making future re-runs of the same SHA
un-actionable from the existing branch name.
Refs CAR-1195 (CTO fix#2).
deploy-dev and deploy-uat had CI_GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
which is the package-scoped container-registry token. PR creation and
auto-merge against cartsnitch/infra would 403 on the first real push.
Bind to secrets.CI_GITEA_TOKEN (the token the infra checkout already
uses for branch push) so the Gitea API calls have repo-write scope.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Runner pod gitea-act-runner-cartsnitch-85b5984bb-527xw has a corrupt
/root/.cache/act clone of actions/setup-node (missing dist/setup/index.js).
SHA-pinning changed the cache hash but the fresh clone on that pod still
ends up missing the dist directory.
catthehacker/ubuntu:act-latest ships Node pre-installed; the lint job only
needs ESLint + tsc, both of which are devDependencies installed by npm ci.
Removing actions/setup-node from lint bypasses the corrupt pod cache entirely
without affecting other jobs.
Refs CAR-1162
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The CI's npm audit (10.8.2) flagged three transitive vulnerabilities
that local newer-npm runs (11.x) miss due to advisory-DB divergence:
- @babel/plugin-transform-modules-systemjs: 7.29.0 -> ^7.29.4
(CVE-2026-44728: arbitrary code generation, fixed in 7.29.4)
- fast-uri: 3.1.0 -> ^3.1.2
(path traversal / host confusion via percent-encoded segments)
- brace-expansion: 5.0.5 -> >=5.0.6
(DoS via large numeric range defeating max protection)
These are non-breaking transitive updates within the same major
version. The previous override for brace-expansion (>=1.1.13) was
too loose to exclude 5.0.2-5.0.5; tightening it to >=5.0.6.
Ref CAR-1162, CAR-1122, CAR-1078
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Remove .mcp.json (scope creep, unrelated to CAR-1078)
- Bump vitest to ^4.1.8 (fixes GHSA-5xrq-8626-4rwp critical)
- Run npm audit fix for non-breaking vulns
- Pin actions/checkout and actions/setup-node to commit SHAs
in .gitea/workflows/ci.yml to force a clean cache fetch on
the act runner (workaround for corrupted /root/.cache/act cache)
Refs CAR-1162, CAR-1122, CAR-1078
email_worker calls get_async_session_factory() inside every resolve_user()
call, which creates a brand-new async engine (and thus a brand-new
connection pool) on every message. In a tight consumer loop processing
5 messages per batch, this rapidly exhausts DragonflyDB/Postgres
connection limits and manifests as ConnectionResetError.
Fix: cache the async engine in a module-level dict keyed by URL in
cartsnitch_common.database:get_async_engine(), matching the pattern
already used in receiptwitness:events.py for the Redis connection pool.
Also add pool_size=10, max_overflow=20, pool_pre_ping=True for
健壮连接管理.
Similarly, receiptwitness/queue/email.py:get_redis() was creating a new
Redis connection on every call with no pooling. Share a
ConnectionPool (max_connections=30) across all get_redis() callers.
Fixes CAR-1078
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Merge PR #266: Fix API crash — remove dead dispose_engine import
Removes non-existent dispose_engine import from main.py that caused ImportError and CrashLoopBackOff on API pods.
Reviewed-by: Checkout Charlie (QA PASS)
Approved-by: Savannah Savings (CTO)
ImportError: cannot import name 'dispose_engine' from 'cartsnitch_api.database'
The function does not exist and is unused - lifespan already calls close_db() directly.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
cpfarhood confirmed the Gitea registry token is configured as REGISTRY_TOKEN
(not GITEA_TOKEN). This applies to both the registry docker login steps
and the infra repo checkout steps in deploy-dev/deploy-uat.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Replace hardcoded 'cs_carl' Gitea registry username with '${{ github.actor }}' in all 5 login steps
- Use kustomize 'OLD=NEW:TAG' rename syntax so existing ghcr.io image entries are updated instead of duplicated
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Gitea's GITHUB_TOKEN authenticates against git.farh.net, not ghcr.io.
Use explicit GHCR_USERNAME and GHCR_TOKEN secrets instead.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Replace all runs-on: runners-cartsnitch with ubuntu-latest
- Remove SARIF upload steps (no Gitea Security tab)
- Replace GitHub App token with secrets.GITEA_TOKEN in deploy-dev and deploy-uat
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Exclude src/__tests__ from tsconfig to prevent test files from being
compiled during Docker build. Fixes build-and-push-auth CI failure.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add build-and-push-auth job and update deploy-dev/uat to include auth image.
- Add AUTH_IMAGE_NAME env var
- Add build-and-push-auth job (modeled on build-and-push-api)
- Add build-and-push-auth to deploy-dev and deploy-uat needs
- Add auth image tag determination and update steps in both deploy jobs
- Update commit messages to include auth
UAT PASS (Deal Dottie, 2026-05-04) + Security PASS (Stockboy Steve, 2026-05-04)
Merged with admin privileges due to 1-commit divergence (README/UI-only release commit from PR #245 with no file overlap with uat changes). No functional conflict.
Refs: CAR-842, CAR-812
- docs: fix email address format to receipts+<token>@receipts.cartsnitch.com
(per Settings → Receipt Email UI, not the old farh.net domain format)
- docs: fix UI section label from 'Account' to 'Receipt Email'
- scripts/seed-env.sh: fix --env flag parser when called as './seed-env.sh --env uat'
positional form was already correct; flag form was consuming --env as ENV value
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add /auth/health as additional health check route (Envoy forwards full path)
- Change db status 'connected' to 'reachable' to match health.test.ts
- Only pass /auth/* routes to Better-Auth handler to prevent 404 on unknown routes
Co-Authored-By: Paperclip <noreply@paperclip.ing>