nginxinc/nginx-unprivileged sets USER nginx internally, but the kubelet
cannot resolve non-numeric string usernames against OCI image config at
container-create time. With runAsNonRoot: true, K3s kubelet reports:
"container has runAsNonRoot and image will run as root"
Fix: explicitly add USER 101 after the COPY steps. UID 101 is the numeric
UID that nginx-unprivileged's nginx user already runs as — this instruction
just makes it visible in the final OCI image config layer so the kubelet
can verify non-root without username resolution.
Companion infra PR cartsnitch/infra#77 adds runAsUser: 101 as immediate
unblock while this Dockerfile change propagates through CI.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Non-root users cannot bind to ports < 1024. Port 8080 is used by
nginxinc/nginx-unprivileged by default.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Switch from nginx:stable-alpine to nginxinc/nginx-unprivileged:stable-alpine.
The unprivileged image runs as nginx user (UID 101) on port 8080, satisfying
the runAsNonRoot: true security context in Kubernetes.
Fixes: https://github.com/cartsnitch/infra/issues/65
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Full Twitter/X and Reddit promotional copy for all 5 shrinkflation
series posts (anchor top-10, dairy, frozen, household, snacks).
Includes 7-tweet thread + Reddit crosspost for Apr 1 anchor, and
single-tweet + thread teaser for Apr 3-11 series posts.
Refs: CAR-202, CAR-170, CAR-199
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Update frontmatter and footer navigation for dairy, frozen food,
household essentials, and snacks posts to match the cereal post series
format. Sets consistent series name "The Shrinkflation Files", correct
part numbers (2–5), and properly linked prev/next nav footers.
Refs: CAR-157, CAR-114
Co-authored-by: Frontend Frankie <frankie@cartsnitch.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* content: add founder story blog post — Why We Built CartSnitch
Replaces the Phase 1 draft with the final founder story from CMO
content-spec (CAR-134). Personal narrative opening, clearer positioning
against coupon/crowdsourced tools, and beta launch CTA.
Refs: CAR-134, CAR-114
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* content: merge founder story with data stats per Penny's review (v1.1)
Restores BLS/USDA statistics, specific shrinkflation examples, and
privacy footer from the original draft. Keeps the founder pasta story,
three-things framework, and cleaner positioning from the CMO content-spec.
Combined version addresses all points raised in Penny's changes-requested review.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Frontend Frankie <frankie@cartsnitch.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
* content: add shrinkflation series post 1 — The Shrinkflation Files: Cereal
Updates cereal blog post with final content-spec v1.0 from CAR-141.
Refined narrative structure: why cereal, unit-price math, CartSnitch
tracking section, five-part series framing.
Part of shrinkflation series (CAR-141, parent CAR-114).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* content: update cereal shrinkflation post to v1.1 with brand-specific data
Restores brand data table (Cheerios, Frosted Flakes, Lucky Charms, etc. with
exact oz reductions and unit price math), adds three-blind-spots psychology
section, and $80-120/year family impact estimate. Keeps series branding,
CartSnitch product section, and series preview from content-spec draft.
Addresses CEO changes-requested review on PR #29.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Frontend Frankie <frankie@cartsnitch.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Adds marketing blog post comparing CartSnitch, Flipp, Basket, and Ibotta.
Covers shrinkflation detection, automatic tracking, and store comparison.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Onboarding guides cover the five core user flows: getting started,
connecting store accounts, setting up price alerts, reading the
dashboard, and comparing stores. FAQ addresses common questions
about how CartSnitch works, data privacy, supported stores, and
troubleshooting.
All guides include screenshot placeholders for integration once
staging is available (blocked on CAR-60).
Ref: CAR-114
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The build-and-push job pulls nginx:stable-alpine from Docker Hub during
docker build. Anonymous pulls hit rate limits on self-hosted runners.
Add docker/login-action for Docker Hub using org secrets before the
build step (unconditional — needed for both PR and push builds).
Closes#22
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Tag container images with YYYY.MM.DD CalVer format on merge to main,
with build number suffix for same-day collisions. Creates matching
git tags (vYYYY.MM.DD). Retains latest tag as convenience alias.
GitHub issue: cartsnitch/infra#24
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Copy 10 marketing content files from the cmo/content-phase1 branch
of cartsnitch/agents into content/marketing/, preserving the
blog/, email/, and social/ subdirectory structure.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The correct self-hosted ARC runner label is runners-cartsnitch, not
cartsnitch-runners. All CI jobs were failing because no runners
matched the old label.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
We push to GHCR only per infrastructure policy. The Docker Hub login
step was added in error and would fail since DOCKERHUB_USERNAME/TOKEN
secrets are not configured.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Confirmed secrets are length 0 from CI runners. Docker Hub auth
cannot work until secrets are properly scoped to these repos.
Refs: CAR-77
Co-Authored-By: Paperclip <noreply@paperclip.ing>
DOCKERHUB_USERNAME/DOCKERHUB_TOKEN secrets are not accessible from
the self-hosted runners. Remove credentials blocks and login steps
to avoid template validation failures. Docker Hub pulls will use
anonymous access.
Refs: CAR-77
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Docker Hub login step is now conditional on secret existence
to avoid failures when org secrets are not yet provisioned.
Refs: CAR-77
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Avoids Docker Hub 429 rate limits by pulling node:20-alpine and
nginx:stable-alpine from ghcr.io/cartsnitch/mirror/. GHCR login
now runs on all builds (not just main push) to authenticate pulls.
Ref: cartsnitch/infra#7, CAR-55
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The build-and-push job had an unconditional Docker Hub login step that
was failing because DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets are
not provisioned. Since we push images to GHCR (not Docker Hub), this
step is not needed.
Closescartsnitch/infra#5
Co-authored-by: deploy-debbie[bot] <268472978+deploy-debbie[bot]@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
The build-and-push job pulls node:20-alpine and nginx:stable-alpine from
Docker Hub during docker build. Without authentication these pulls hit
the unauthenticated rate limit, causing intermittent build failures.
Closes#8
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Build stage uses node:20-alpine to install deps and build.
Prod stage uses nginx:stable-alpine to serve static assets.
Includes nginx config with SPA routing, gzip, health endpoint,
and aggressive caching for Vite-hashed assets.
Closes#6
Co-Authored-By: Paperclip <noreply@paperclip.ing>