# Secrets and environment configuration

This document describes how AGNET manages secrets across local development, CI, and production. **Never commit real credentials to git.**

## Local development

Copy `.env.local.example` to `.env.local`. Local values are for development only:

| Variable | Purpose | Local source |
|----------|---------|--------------|
| `CORE_API_URL` | NestJS core-api base URL | `http://localhost:4000` |
| `CORE_API_USE_MOCK` | In-memory BFF mock (no backend) | `true` for frontend-only |
| `DATABASE_URL` | PostGIS connection for `dev:stack` | Docker compose in backend repo |
| `JWT_SECRET` | core-api JWT signing (backend) | Backend `.env` — use a random 32+ char string |
| `TWILIO_ACCOUNT_SID` | Twilio SMS (production OTP) | Leave unset locally; use dev OTP `1234` |
| `TWILIO_AUTH_TOKEN` | Twilio API token | Leave unset locally |
| `TWILIO_FROM_NUMBER` | Twilio sender number | Leave unset locally |
| `SMS_PROVIDER` | `dev` (default) or `twilio` | Unset in local dev |

The BFF reads secrets from environment variables at runtime via Next.js (`process.env`). See [`lib/sms-provider.ts`](../lib/sms-provider.ts) for OTP provider wiring.

## Production vault guidance

Store all production secrets in a managed secrets vault. Do **not** use `.env` files on production hosts.

### Recommended vaults

| Platform | Service | Notes |
|----------|---------|-------|
| AWS | AWS Secrets Manager or SSM Parameter Store | Inject via ECS task secrets or Lambda env from ARN |
| GCP | Secret Manager | Mount as env vars in Cloud Run / GKE |
| Azure | Key Vault | Reference from App Service / Container Apps |
| Generic | HashiCorp Vault | Use agent sidecar or CI OIDC auth |

### Secrets inventory (production)

| Secret | Used by | Rotation |
|--------|---------|----------|
| `JWT_SECRET` | core-api | Quarterly or on compromise |
| `DATABASE_URL` | core-api, Prisma | On credential rotation |
| `TWILIO_ACCOUNT_SID` / `TWILIO_AUTH_TOKEN` / `TWILIO_FROM_NUMBER` | core-api SMS adapter | On provider rotation |
| `S3_*` / object storage keys | documents-service | On credential rotation |
| Webhook HMAC secrets | core-api inbound webhooks | Per integration |

### Injection pattern

1. Define secrets in the vault with environment-specific paths (e.g. `agnet/prod/jwt-secret`).
2. CI/CD or orchestrator injects them as environment variables at deploy time — not baked into images.
3. Restrict IAM so only the core-api and documents-service roles can read their respective secrets.
4. Enable audit logging on vault access.

### CI

- Use GitHub Actions **encrypted secrets** for non-production E2E (test DB URL, Playwright base URL).
- Never echo secret values in CI logs.
- Prefer OIDC federation to cloud vaults over long-lived access keys.

## Related code

| Module | Role |
|--------|------|
| [`lib/sms-provider.ts`](../lib/sms-provider.ts) | OTP/SMS abstraction (dev + Twilio stub) |
| [`lib/dev-api-mock.ts`](../lib/dev-api-mock.ts) | Dev OTP `1234` when `CORE_API_USE_MOCK=true` |
| [`scripts/start-dev.sh`](../scripts/start-dev.sh) | Local stack orchestration |

## Checklist before production

- [ ] `JWT_SECRET` is a cryptographically random value (≥ 256 bits)
- [ ] `CORE_API_USE_MOCK` is **not** set in production
- [ ] Twilio (or local UZ SMS provider) credentials are in vault
- [ ] Database credentials use least-privilege DB user
- [ ] `.env.local` and `.env` are in `.gitignore` (verified)
