BREAKING CHANGE: All environment variables are now runtime-configurable
Changes:
- Removed ALL build-time NEXT_PUBLIC_* variables from Dockerfile and CI/CD
- Created server-side proxy routes for Wren API (/api/wren/*)
- Refactored wrenClient.ts to use proxy endpoints (reduced from 400+ to 200 lines)
- Updated checkoutClient.ts and emailClient.ts to remove NEXT_PUBLIC_ fallbacks
- Hardcoded metadataBase in layout.tsx (no longer depends on env var)
- Updated .env.local to use runtime-only variables (WREN_API_TOKEN, NocoDB config)
Security improvements:
- Wren API token never exposed to browser
- All secrets stay server-side
- No sensitive data baked into build
Configuration:
- Wren API: Set WREN_API_TOKEN in docker-compose or .env
- NocoDB: Set NOCODB_* variables in docker-compose or .env
- No Gitea secrets/variables needed for build (only registry credentials)
Docker build is now truly environment-agnostic - same image works in
any environment with different runtime configuration.
Security & Cleanup Changes:
1. Removed NEXT_PUBLIC_WREN_API_TOKEN from frontend (security risk)
2. Removed Formspree references (no longer needed)
3. Wren API token now lives in backend only (runtime configurable)
4. Added NocoDB env vars to frontend for admin portal server-side API
Changes:
- Dockerfile: Removed Formspree and NEXT_PUBLIC_WREN_API_TOKEN build args
- CI/CD: Updated build-args to only include necessary variables
- Frontend should call backend /api/wren/* endpoints
- Backend handles Wren API with WREN_API_TOKEN (can change anytime!)
Benefits:
✅ API token no longer exposed in browser
✅ Can change Wren token without rebuilding images
✅ Cleaner build process
✅ Removed unused Formspree dependencies
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem:
- NEXT_PUBLIC_* variables must be baked into Next.js build at BUILD TIME
- Setting them in docker-compose is too late (bundle already built)
- This caused "NEXT_PUBLIC_WREN_API_TOKEN is undefined" errors in production
Solution:
1. Updated Dockerfile to accept ARG values for all NEXT_PUBLIC_* variables
2. Set ARGs as ENV variables before npm run build (lines 15-26)
3. Updated CI/CD workflow to pass build-args from Gitea secrets/vars
4. Variables are now baked into the image during build
Next Steps:
1. Add these secrets to Gitea repository settings:
- NEXT_PUBLIC_WREN_API_TOKEN
- NEXT_PUBLIC_FORMSPREE_CONTACT_ID
- NEXT_PUBLIC_FORMSPREE_OFFSET_ID
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
2. Add this variable to Gitea repository settings:
- NEXT_PUBLIC_API_BASE_URL
3. Next push will build image with variables baked in
4. Can simplify docker-compose (remove NEXT_PUBLIC_* from web service)
Files Changed:
- Dockerfile: Added ARG and ENV declarations before build step
- .gitea/workflows/build-deploy.yml: Added build-args to frontend image build
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change buildcache from mode=max to mode=min to reduce cache layer size
and fix "413 Request Entity Too Large" error when pushing to Gitea registry.
mode=min only caches layers likely to be reused, significantly reducing
the cache size while maintaining build performance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL SECURITY FIXES:
- Add webhook secret validation to prevent signature bypass
- Implement idempotency protection across all webhook handlers
- Add atomic database updates to prevent race conditions
- Improve CORS security with origin validation and logging
- Remove .env from git tracking to protect secrets
STRIPE INTEGRATION:
- Add support for checkout.session.expired webhook event
- Add Stripe publishable key to environment configuration
- Fix webhook handlers with proper idempotency checks
- Update Order model with atomic updatePaymentAndStatus method
- Add comprehensive logging for webhook processing
DEPLOYMENT ARCHITECTURE:
- Split into two Docker images (frontend-latest, backend-latest)
- Update CI/CD to build separate frontend and backend images
- Configure backend on port 3801 (internal 3001)
- Add production-ready docker-compose.yml
- Remove redundant docker-compose.portainer.yml
- Update nginx configuration for both frontend and backend
DOCUMENTATION:
- Add PRODUCTION-SETUP.md with complete deployment guide
- Add docs/stripe-security-fixes.md with security audit details
- Add docs/stripe-checkout-sessions.md with integration docs
- Add docs/stripe-webhooks.md with webhook configuration
- Update .env.example with all required variables including Stripe publishable key
CONFIGURATION:
- Consolidate to single .env.example template
- Update .gitignore to protect all .env variants
- Add server/Dockerfile for backend container
- Update DEPLOYMENT.md with new architecture
🔒 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Match working workflow pattern from other server.
Let runner provide Docker environment directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Let Buildx use default driver with --privileged container.
The 'docker' driver caused endpoint property errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Runner already mounts /var/run/docker.sock automatically.
Keep only --privileged flag to allow socket access.
Fixes: Error response from daemon: Duplicate mount point
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --privileged flag and explicit Docker socket mount to container
- Configure Buildx to use 'docker' driver instead of 'docker-container'
- This avoids nested container permission issues while using full Ubuntu image
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Match the working workflow pattern from other server by using
repository variables instead of hardcoded values:
- REGISTRY_HOST (code.puffinoffset.com)
- REGISTRY_USERNAME (matt)
- IMAGE_NAME (puffin-app)
- REGISTRY_TOKEN (secret with write:package permission)
This makes the workflow portable and matches the proven
working configuration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Based on Zen multi-model consensus analysis:
Root cause: act_runner v0.2.13+3 maps ubuntu-latest to bare
ubuntu:22.04 which lacks Docker CLI. Newer runners use full images.
Solution: Override job container to use ghcr.io/catthehacker/ubuntu:full-22.04
which includes Docker, Buildx, Node.js, and standard CI tools.
This is the recommended approach from act_runner maintainers for
GitHub Actions compatibility.
Consensus from Gemini-2.5-pro and o3 models.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switch to the pattern that works on other server:
- Run directly on ubuntu-latest (no container)
- Use docker/login-action@v3
- Use docker/setup-buildx-action@v3
- Use docker/build-push-action@v6
This matches the working workflow from another repository
and should work with the Gitea runner configuration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The docker:dind image doesn't include Node.js which is required
by actions/checkout@v4. Use manual git commands instead.
Changes:
- Install git via apk (Alpine package manager)
- Clone repository directly
- Checkout specific commit SHA
This approach works with docker:dind's minimal Alpine base.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: act_runner provides minimal environment without sudo
or Docker CLI. Cannot install packages in workflow.
Solution: Use docker:24-dind container which includes Docker CLI
and daemon. Runs with --privileged to allow nested containers.
Changes:
- Use docker:24-dind as job container
- Remove installation steps (Docker pre-installed)
- Keep simple login, build, push workflow
Also added alternative solution file showing how to configure
runner with Docker CLI for better performance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: Gitea act_runner mounts Docker socket but doesn't
include Docker CLI by default to keep the image lightweight.
Solution: Install docker-ce-cli package before running any
docker commands. This allows the workflow to communicate with
the Docker daemon via the mounted socket.
Changes:
- Add step to install Docker CLI from official Docker repository
- Verify installation with docker version
- Continue with login, build, and push steps
Based on Zen expert analysis of the runner environment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace Kaniko approach with direct Docker CLI commands.
The Gitea runner mounts Docker socket, making Docker available
in ubuntu-latest environment.
Key changes:
- Remove container specification (Kaniko lacks shell utilities)
- Use docker login with password-stdin for authentication
- Build with docker build using multiple -t tags
- Push both latest and commit SHA tags
- Works with Docker-based Gitea runners
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Kaniko executor image doesn't have traditional user management
and runs with appropriate permissions by default.
Fixes: unable to find user root: no matching entries in passwd file
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace Docker-based build with Kaniko executor to support
Docker-based Gitea runners that don't have Docker daemon access.
Kaniko builds container images without requiring Docker,
making it ideal for containerized CI environments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create Gitea Actions workflow for automated Docker builds on push to main
- Add docker-compose.portainer.yml for production Portainer deployment
- Create comprehensive DEPLOYMENT.md guide with step-by-step instructions
- Update CLAUDE.md with CI/CD pipeline documentation
Images are built and pushed to Gitea registry at:
code.puffinoffset.com/matt/puffin-app:latest
code.puffinoffset.com/matt/puffin-app:main-<sha>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>