Skip to content

Commit 7e3186c

Browse files
authored
chore(env): harmonize example env files and fix OSV findings (#142)
* chore(env): harmonize example env files and fix OSV findings - Add env-files cursor rule, block-secret-files/gitleaks/trufflehog patterns - API: .env.defaults.example, .env.test.example; remove .env-sample - Web/mobile/docu: aligned .env.*.example and docs - pnpm overrides + @next/eslint-plugin-next for flatted, h3, next, socket.io-parser, undici * chore(security): tighten trufflehog env ignores, drop docs/plans - Restrict .trufflehogignore to committed env template paths - Align scripts/README with block-secret-files and TruffleHog - Clarify apps/web README env docs; remove docs/plans plan file
1 parent f89bca4 commit 7e3186c

26 files changed

+553
-327
lines changed

.cursor/rules/base/env-files.mdc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
description: Environment files — templates, naming, alignment with t3 createEnv
3+
glob: "**/.env.*.example"
4+
alwaysApply: false
5+
---
6+
7+
## Source of truth
8+
9+
- Runtime vars: each app’s `createEnv` in `lib/env.ts` or `src/lib/env.ts` (t3-oss + Zod). Adding/removing a var **requires** updating that schema and the committed template(s) for that app.
10+
- Do not document env vars only in README without a template line.
11+
12+
## Secrets and commits
13+
14+
- **Never** put real API keys, tokens, or production secrets in committed `.env.<qualifier>.example` templates, nor in `.env.development`, `.env.staging`, or `.env.production`. Use obvious placeholders (`sk-ant-xxx`, `your_dynamic_environment_id`).
15+
- `.env`, `.env.local`, and secret-bearing `.env.test` stay gitignored per app; ship `.env.test.example` where tests need a template.
16+
17+
## Naming (templates) — pattern `.env.<qualifier>.example`
18+
19+
- **Canonical glob**: `**/.env.*.example` (Cursor: `@**/.env.*.example`). Filenames must be `.env.` + one qualifier + `.example` (e.g. `defaults`, `local`, `test`). Do **not** add `.env.sample`, `.env-example`, or bare `.env.example`.
20+
- **Fastify API** (`apps/api`): `.env.defaults.example` (copy to `.env`); `.env.test.example` → `.env.test`.
21+
- **Next.js** (`apps/web`, `apps/mathler`): `.env.local.example` (copy to `.env.local`); committed `.env.development` / `.env.staging` / `.env.production` only for non-sensitive defaults (`NEXT_PUBLIC_*`, etc.).
22+
- **Expo** (`apps/mobile`): `.env.defaults.example` with `EXPO_PUBLIC_*`.
23+
- **Docu**: committed `.env.development`; optional `.env.defaults.example` aligned with `lib/env.ts`.
24+
- **Legacy**: `.env.schema` may remain; update `block-secret-files` / gitleaks when renaming templates.
25+
26+
## Next.js section order (web + mathler)
27+
28+
Use the same header order in `.env.local.example` and committed `.env.*`:
29+
30+
1. Short header (what is committed vs local-only).
31+
2. Client / `NEXT_PUBLIC_*` (API URL, auth, logging, legal).
32+
3. Server-only / local secrets (`JWT_SECRET`, etc.) — template only where applicable.
33+
4. E2E / Playwright (`PLAYWRIGHT_APP_URL`, `PLAYWRIGHT_API_URL` on web; unify mathler on the same names; avoid duplicate `BASE_URL` / `PLAYWRIGHT_TEST_BASE_URL` without documented precedence).
34+
5. Optional integrations (Sentry, …).
35+
36+
## Prefix discipline
37+
38+
- Browser-exposed: `NEXT_PUBLIC_*` (Next), `EXPO_PUBLIC_*` (Expo). Do not use unprefixed names for client-only config.
39+
- If `createEnv` expects `NEXT_PUBLIC_LOG_*`, do not use `LOG_*` alone in Next env files.
40+
41+
## API template completeness
42+
43+
- `apps/api/.env.defaults.example` and `.env.test.example` share **the same section headings**; defaults template documents the full `env.ts` surface (optional blocks commented). Tests override a subset via `.env.test`.
44+
45+
## When editing
46+
47+
- After changing any template, verify `pnpm` dev/test for that app still loads `createEnv` without missing/empty required vars.
48+
- Update docs under `apps/docu/` if canonical filenames or copy instructions change.

.gitleaks.toml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@ useDefault = true
1111
description = "Allowlist for test fixtures and example files"
1212
paths = [
1313
'''apps/web/e2e/wallet-setup/.*\.(ts|js|mjs)$''',
14-
'''\.env-example$''',
15-
'''\.env\.example$''',
16-
'''\.env\.test\.example$''',
17-
'''\.env-sample$''',
18-
'''^apps/web/\.env\.local\.example$''',
19-
'''\.env\.sample$''',
14+
'''\.env\.[^/]+\.example$''',
2015
'''\.env\.schema$''',
2116
'''\.env\.test$''',
2217
'''\.github/workflows/.*\.yml$''',

.trufflehogignore

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@
77
^\.cursor/skills/.*
88
^packages/.*/README\.md$
99

10-
# Example and sample files
11-
.*\.example$
12-
.*\.sample$
13-
.*\.env-example$
14-
.*\.env\.example$
10+
# Committed env templates and defaults (match scripts/block-secret-files.mjs allowlist)
11+
.*\.env\.[^/]+\.example$
1512
.*\.env\.schema$
16-
.*\.env\.test$
13+
.*\.env\.(development|staging|production|test)$
1714

1815
# Test files
1916
.*\.test\.(ts|tsx|js|jsx|mjs)$

apps/api/.env-sample

Lines changed: 0 additions & 63 deletions
This file was deleted.

apps/api/.env.defaults.example

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copy to .env and adjust. Placeholders only — never commit real secrets.
2+
# Schema: apps/api/src/lib/env.ts (t3 createEnv)
3+
4+
# -----------------------------------------------------------------------------
5+
# Core
6+
# -----------------------------------------------------------------------------
7+
PORT=3001
8+
HOST=0.0.0.0
9+
# NODE_ENV=development
10+
# CI=false
11+
12+
# -----------------------------------------------------------------------------
13+
# Database (PGLITE=true uses in-memory DB; else DATABASE_URL required)
14+
# -----------------------------------------------------------------------------
15+
PGLITE=false
16+
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
17+
18+
# -----------------------------------------------------------------------------
19+
# Ops / limits / logging
20+
# -----------------------------------------------------------------------------
21+
# RATE_LIMIT_MAX=100
22+
# RATE_LIMIT_TIME_WINDOW=60000
23+
# TRUST_PROXY=true
24+
# SECURITY_HEADERS_ENABLED=true
25+
# BODY_LIMIT=1048576
26+
# REQUEST_TIMEOUT=30000
27+
# LOG_LEVEL=info
28+
29+
# -----------------------------------------------------------------------------
30+
# Sentry (optional)
31+
# -----------------------------------------------------------------------------
32+
# SENTRY_DSN=
33+
# SENTRY_ENVIRONMENT=
34+
35+
# -----------------------------------------------------------------------------
36+
# AI (at least one provider path for chat features)
37+
# -----------------------------------------------------------------------------
38+
ANTHROPIC_API_KEY=sk-ant-xxx
39+
# OPEN_ROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxx
40+
# OLLAMA_BASE_URL=http://localhost:11434
41+
# AI_PROVIDER=anthropic
42+
# AI_DEFAULT_MODEL=
43+
# AI_UPSTREAM_TIMEOUT_MS=120000
44+
# BRAVE_SEARCH_API_KEY=
45+
46+
# -----------------------------------------------------------------------------
47+
# JWT / encryption (JWT_SECRET must match Next.js web app)
48+
# -----------------------------------------------------------------------------
49+
JWT_SECRET=default-jwt-secret-min-32-chars-for-dev
50+
# 64 hex chars dev default; must not be all-zero in production
51+
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
52+
# ACCESS_JWT_EXPIRES_IN_SECONDS=900
53+
# REFRESH_JWT_EXPIRES_IN_SECONDS=604800
54+
# JWT_ISSUER=api.yourapp.com
55+
# JWT_AUDIENCE=api.yourapp.com
56+
57+
# -----------------------------------------------------------------------------
58+
# Email / app identity
59+
# -----------------------------------------------------------------------------
60+
# RESEND_API_KEY=re_placeholder
61+
# EMAIL_FROM=noreply@localhost
62+
# EMAIL_FROM_NAME=App
63+
# APP_NAME=Your App
64+
65+
# -----------------------------------------------------------------------------
66+
# Testing / features
67+
# -----------------------------------------------------------------------------
68+
# ALLOW_TEST=false
69+
70+
# -----------------------------------------------------------------------------
71+
# OAuth — optional (routes return 503 when unset)
72+
# -----------------------------------------------------------------------------
73+
# GITHUB_CLIENT_ID=
74+
# GITHUB_CLIENT_SECRET=
75+
# OAUTH_GITHUB_CALLBACK_URL=http://localhost:3000/auth/callback/oauth/github
76+
# OAUTH_GITHUB_CALLBACK_URLS=http://localhost:3000/auth/callback/oauth/github,yourapp://auth/callback
77+
78+
# GOOGLE_CLIENT_ID=
79+
# GOOGLE_CLIENT_SECRET=
80+
# OAUTH_GOOGLE_CALLBACK_URL=http://localhost:3000/auth/callback/oauth/google
81+
# OAUTH_GOOGLE_CALLBACK_URLS=http://localhost:3000/auth/callback/oauth/google,yourapp://auth/callback
82+
83+
# FACEBOOK_CLIENT_ID=
84+
# FACEBOOK_CLIENT_SECRET=
85+
# OAUTH_FACEBOOK_CALLBACK_URL=http://localhost:3000/auth/callback/oauth/facebook
86+
# OAUTH_FACEBOOK_CALLBACK_URLS=http://localhost:3000/auth/callback/oauth/facebook,yourapp://auth/callback
87+
88+
# TWITTER_CLIENT_ID=
89+
# TWITTER_CLIENT_SECRET=
90+
# OAUTH_TWITTER_CALLBACK_URL=http://localhost:3000/auth/callback/oauth/twitter
91+
# OAUTH_TWITTER_CALLBACK_URLS=http://localhost:3000/auth/callback/oauth/twitter,yourapp://auth/callback
92+
93+
# -----------------------------------------------------------------------------
94+
# CORS / app URLs / auth metadata
95+
# -----------------------------------------------------------------------------
96+
# ALLOWED_ORIGINS=*
97+
# TOTP_ISSUER=
98+
# WEBAUTHN_RP_NAME=

apps/api/.env.test.example

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,65 @@
1-
# Test env - copy to .env.test (gitignored)
1+
# Test env — copy to .env.test (gitignored under apps/api). Placeholders only.
2+
# Section headers match apps/api/.env.defaults.example
3+
4+
# -----------------------------------------------------------------------------
5+
# Core
6+
# -----------------------------------------------------------------------------
27
NODE_ENV=test
8+
# CI=false
9+
10+
# -----------------------------------------------------------------------------
11+
# Database
12+
# -----------------------------------------------------------------------------
313
PGLITE=true
414
DATABASE_URL=postgresql://localhost/test
5-
JWT_SECRET=e2e-jwt-secret-min-32-chars-for-tests
615

7-
# AI provider: Anthropic (default), Open Router, or Ollama (use .env.test, not committed)
8-
# Unit/E2E tests use Anthropic Sonnet. Set ANTHROPIC_API_KEY for local and CI.
16+
# -----------------------------------------------------------------------------
17+
# Ops / limits / logging
18+
# -----------------------------------------------------------------------------
19+
# RATE_LIMIT_MAX=10000
20+
21+
# -----------------------------------------------------------------------------
22+
# Sentry (optional)
23+
# -----------------------------------------------------------------------------
24+
# SENTRY_DSN=
25+
# SENTRY_ENVIRONMENT=
26+
27+
# -----------------------------------------------------------------------------
28+
# AI
29+
# -----------------------------------------------------------------------------
930
ANTHROPIC_API_KEY=sk-ant-xxx
10-
# OPEN_ROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxx
31+
# OPEN_ROUTER_API_KEY=
1132
# OLLAMA_BASE_URL=http://localhost:11434
12-
13-
# Optional: Override default AI model (Claude Sonnet 4)
1433
# AI_DEFAULT_MODEL=claude-sonnet-4-20250514
1534

16-
# Optional: GitHub OAuth (for OAuth route tests)
35+
# -----------------------------------------------------------------------------
36+
# JWT / encryption
37+
# -----------------------------------------------------------------------------
38+
JWT_SECRET=e2e-jwt-secret-min-32-chars-for-tests
39+
# ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
40+
41+
# -----------------------------------------------------------------------------
42+
# Email / app identity
43+
# -----------------------------------------------------------------------------
44+
# RESEND_API_KEY=
45+
# EMAIL_FROM=
46+
# APP_NAME=
47+
48+
# -----------------------------------------------------------------------------
49+
# Testing / features
50+
# -----------------------------------------------------------------------------
51+
# ALLOW_TEST=false
52+
53+
# -----------------------------------------------------------------------------
54+
# OAuth (optional — for OAuth route tests)
55+
# -----------------------------------------------------------------------------
1756
# GITHUB_CLIENT_ID=
1857
# GITHUB_CLIENT_SECRET=
1958
# OAUTH_GITHUB_CALLBACK_URL=http://localhost:3000/auth/callback/oauth/github
2059

21-
# CORS + URL allowlist. Default: * (any). Comma-separated origins to restrict.
60+
# -----------------------------------------------------------------------------
61+
# CORS / app URLs / auth metadata
62+
# -----------------------------------------------------------------------------
2263
# ALLOWED_ORIGINS=https://app.example.com,https://localhost:3000
23-
24-
# Passkey and TOTP tests (unit tests)
2564
WEBAUTHN_RP_NAME=Test App
2665
TOTP_ISSUER=Test App
27-
28-
# Rate limit (unit tests make many requests; default 100 is too low)
29-
# RATE_LIMIT_MAX=10000

apps/api/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Type-safe REST API built with Fastify & OpenAPI. Routes in `src/routes/` are the
44

55
## Development
66

7-
Start database first (`pnpm db:start`), then `pnpm dev`. Uses Supabase CLI for PostgreSQL, or `PGLITE=true` for in-memory. Dev server at [http://localhost:3000](http://localhost:3000).
7+
Copy [`.env.defaults.example`](.env.defaults.example) to `.env` and set values (gitignored). Start database first (`pnpm db:start`), then `pnpm dev`. Uses Supabase CLI for PostgreSQL, or `PGLITE=true` for in-memory. Dev server at [http://localhost:3000](http://localhost:3000).
88

99
## Vercel
1010

apps/docu/.env.defaults.example

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copy to .env.local if needed. Schema: apps/docu/lib/env.ts
2+
3+
# -----------------------------------------------------------------------------
4+
# Client (NEXT_PUBLIC_*)
5+
# -----------------------------------------------------------------------------
6+
NEXT_PUBLIC_SITE_URL=https://docs.basilic.dev
7+
8+
# -----------------------------------------------------------------------------
9+
# Server (local dev)
10+
# -----------------------------------------------------------------------------
11+
PORT=3002

apps/docu/.env.development

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
PORT=3002
1+
# Docu dev server — committed, non-sensitive. Schema: apps/docu/lib/env.ts
2+
PORT=3002
3+
NEXT_PUBLIC_SITE_URL=https://docs.basilic.dev

apps/docu/content/docs/architecture/security.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Pre-commit hooks run automatically via `simple-git-hooks` when you attempt to co
2424

2525
The following file patterns are blocked from being committed:
2626

27-
- `.env` (but `.env-example`, `.env.schema`, `.env.development`, `.env.staging`, `.env.production`, `.env.test` are allowed)
27+
- `.env` (but `.env.<qualifier>.example` templates such as `.env.defaults.example` / `.env.local.example`, `.env.schema`, `.env.development`, `.env.staging`, `.env.production`, `.env.test` are allowed)
2828
- `*.pem`, `*.key`, `*.p12`, `*.pfx`, `*.jks`, `*.keystore`
2929
- `id_rsa*` (SSH private keys)
3030
- Certificate files: `*.crt`, `*.cer`, `*.der`, `*.p7b`, `*.p7c`, `*.p7m`, `*.p7s`
@@ -199,7 +199,7 @@ git reset HEAD~1
199199
### 3. Prevent Future Incidents
200200

201201
- Use `.env` files for secrets (never commit them)
202-
- Use `.env-example` or `.env.schema` for templates
202+
- Use `.env.<qualifier>.example` (e.g. `.env.defaults.example`, `.env.local.example`) or `.env.schema` for templates
203203
- Review staged files before committing: `git diff --cached`
204204
- Run `pnpm security:secrets` before committing
205205

@@ -268,7 +268,7 @@ If CI detects secrets:
268268
## Best Practices
269269

270270
1. **Never commit secrets** - Use `.env` files and environment variables
271-
2. **Use `.env-example`** - Provide templates for required environment variables
271+
2. **Use `.env.<qualifier>.example`** - Provide templates for required environment variables (see `apps/api/.env.defaults.example`, `apps/web/.env.local.example`)
272272
3. **Review before committing** - Check `git diff --cached` before committing
273273
4. **Rotate secrets regularly** - Even if not exposed, rotate secrets periodically
274274
5. **Use secret management** - Consider using tools like HashiCorp Vault, AWS Secrets Manager, etc. for production

0 commit comments

Comments
 (0)