Documentation Index
Fetch the complete documentation index at: https://docs.aspfox.com/llms.txt
Use this file to discover all available pages before exploring further.
Work through every item before announcing your application publicly.
Security
Generate new JWT RSA key pair
Do not use the keys from local development. Generate a fresh pair specifically for production.openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -pubout -out public.pem
base64 -w 0 private.pem # copy to JWT_PRIVATE_KEY
base64 -w 0 public.pem # copy to JWT_PUBLIC_KEY
rm private.pem public.pem
Change default admin credentials
Log in to the production admin account and change the password from Admin123! to something secure. Do not skip this — the default password is the same for every scaffolded project.
Verify ENVIRONMENT=Production
In production, this setting: disables Swagger (do not expose Swagger publicly), suppresses detailed error messages in API responses, causes missing Stripe configuration to throw on startup rather than warn.ASPNETCORE_ENVIRONMENT=Production
Confirm Swagger is not accessible
Navigate to https://api.yourdomain.com/swagger — it should return 404, not the Swagger UI.
Verify HTTPS is configured
- HTTP requests should redirect to HTTPS
- SSL certificate is valid (not self-signed in production)
- HSTS header is present:
Strict-Transport-Security: max-age=31536000
Verify no secrets are in code or config files
Run this from the project root before deploying:git log --all --full-history -- '*.env'
git grep -i "sk_live_"
git grep -i "whsec_"
Any results indicate secrets are committed to git. Rotate those keys immediately.
Stripe
Switch to live Stripe keys
STRIPE_SECRET_KEY must start with sk_live_, not sk_test_. Check in your environment variables.
Webhook endpoint is registered in Stripe Dashboard
Dashboard → Developers → Webhooks. Verify an endpoint exists for your production API URL.
All five required events are selected
The endpoint must be subscribed to exactly these events:
checkout.session.completed
customer.subscription.updated
customer.subscription.deleted
invoice.payment_failed
invoice.payment_succeeded
Webhook signing secret is the live endpoint secret
STRIPE_WEBHOOK_SECRET must start with whsec_ and come from the live endpoint, not from the Stripe CLI. The CLI secret and the Dashboard endpoint secret are different values.
Complete a real test checkout
Use a real card (not a test card) to complete a Pro checkout. Verify the subscription appears in the Stripe Dashboard and the Billing page in your application shows the updated plan.
Email
Sending domain is verified in Resend
Resend Dashboard → Domains. Status must be Verified, not Pending.
EMAIL_FROM_ADDRESS uses your verified domain
Must be noreply@yourdomain.com or similar. Not onboarding@resend.dev.
Send a test email and check deliverability
Register a new account from the public-facing sign-up page. The verification email should arrive within 30 seconds. Check the spam folder — if it lands there, check your SPF/DKIM/DMARC records.
Database
Migrations applied to production database
dotnet ef database update \
--project src/Acme.Infrastructure \
--startup-project src/Acme.Api
The command should print No pending migrations. when complete.Seed data applied
The admin user must exist. Run make seed or the equivalent command against the production database once.
Database backups are configured
If using a managed database (Railway, Render, RDS), enable automatic backups in the provider dashboard. If self-hosting PostgreSQL, configure pg_dump via cron.
OAuth
Production redirect URIs configured in Google Cloud Console
The authorized redirect URI must be https://api.yourdomain.com/api/v1/auth/google/callback. Not localhost.
Production callback URL configured in GitHub OAuth App
The authorization callback URL must be https://api.yourdomain.com/api/v1/auth/github/callback. Not localhost.
Frontend
VITE_API_URL points to production API URL
Must be https://api.yourdomain.com, not http://localhost:5000.
Frontend build uses production environment variables
If using Vite’s build command directly: VITE_API_URL=https://api.yourdomain.com npm run build. Verify the built JavaScript files do not contain localhost.grep -r "localhost" dist/ # should return no results
Notifications and Redis
Redis is running and accessible
The API must be able to connect to Redis. Verify:curl https://api.yourdomain.com/api/v1/notifications/unread-count \
-H "Authorization: Bearer <admin-token>"
# Should return {"success":true,"data":{"count":0}}
# A Redis connection failure causes this to return 500