Configuration¶
Application settings use the FINDINGS_ prefix. The deployment also exposes supporting Docker, port, and Caddy variables through the same .env file.
Environment variables¶
| Variable | Default | Description |
|---|---|---|
POSTGRES_HOST |
See .env.example |
PostgreSQL host used by the backend to build its connection string |
POSTGRES_SERVICE_PORT |
See .env.example |
PostgreSQL container/service port used by the backend connection string |
POSTGRES_USER |
See .env.example |
PostgreSQL username |
POSTGRES_PASSWORD |
See .env.example |
PostgreSQL password |
POSTGRES_DB |
See .env.example |
PostgreSQL database name |
FINDINGS_LOG_LEVEL |
INFO |
Backend log verbosity (DEBUG \| INFO \| WARNING \| ERROR \| CRITICAL, case-insensitive). Invalid values refuse startup. |
FINDINGS_ACCESS_TOKEN_EXPIRE_MINUTES |
5 |
Access token lifetime |
FINDINGS_REFRESH_TOKEN_EXPIRE_DAYS |
7 |
Per-token refresh lifetime; each rotation issues a new token with this expiry (bounded by the family cap below) |
FINDINGS_REFRESH_TOKEN_FAMILY_MAX_LIFETIME_DAYS |
30 |
Security policy. Absolute ceiling on a single login: no amount of rotation extends a refresh-token family past this. When crossed, the family is revoked and the user must log in again. Must be between 7 and 30 inclusive. |
FINDINGS_REFRESH_SESSION_RETENTION_DAYS |
auto (2 x FINDINGS_REFRESH_TOKEN_FAMILY_MAX_LIFETIME_DAYS) |
DB housekeeping. How long already-dead refresh-session rows (expired or revoked) are kept in auth_refresh_sessions before the pruner deletes them. Must be >= 2 x FINDINGS_REFRESH_TOKEN_FAMILY_MAX_LIFETIME_DAYS. Affects forensic/audit window only; has no effect on auth behavior. |
FINDINGS_TRUST_PROXY_HEADERS |
true |
Trust proxy headers (for example from Caddy) to extract real client IPs |
FINDINGS_ALLOWED_ORIGINS |
["http://localhost:5173", "http://localhost:3000"] |
CORS allowed origins |
FINDINGS_ALLOWED_METHODS |
["GET","POST","PATCH","DELETE","OPTIONS"] |
CORS allowed methods |
FINDINGS_ALLOWED_HEADERS |
["Authorization","Content-Type","Accept","If-None-Match"] |
CORS allowed request headers |
FINDINGS_OBJECT_STORAGE_ENDPOINT |
seaweedfs:8333 |
S3-compatible object-storage endpoint |
FINDINGS_OBJECT_STORAGE_ACCESS_KEY |
See .env.example |
Required object-storage access key |
FINDINGS_OBJECT_STORAGE_SECRET_KEY |
See .env.example |
Required object-storage secret key |
FINDINGS_OBJECT_STORAGE_SECURE |
false |
Use HTTPS for object storage |
FINDINGS_OBJECT_STORAGE_EVIDENCE_BUCKET |
finding-attachments |
Bucket for uploaded finding evidence |
FINDINGS_OBJECT_STORAGE_REPORTS_BUCKET |
generated-reports |
Bucket for generated PDF/CSV/JSON exports |
FINDINGS_ATTACHMENT_MAX_FILE_SIZE_MB |
25 |
Authoritative attachment size limit enforced by the backend |
FINDINGS_DB_POOL_SIZE |
5 |
SQLAlchemy connection-pool size per backend instance. Effective max connections per instance = pool_size + max_overflow. |
FINDINGS_DB_MAX_OVERFLOW |
10 |
SQLAlchemy max-overflow burst above pool_size. |
FINDINGS_DB_POOL_PRE_PING |
true |
Issue a small SELECT 1 per checkout to detect stale connections (e.g. after Postgres restart). |
FINDINGS_PASSWORD_MIN_LENGTH |
16 |
Minimum password length. Hard floor: env values below 16 are rejected at startup. |
FINDINGS_PASSWORD_MAX_LENGTH |
128 |
Maximum password length. |
FINDINGS_PASSWORD_MIN_ZXCVBN_SCORE |
3 |
Minimum zxcvbn strength score (0-4). 3 is resistant to GPU-farm brute force for years. |
FINDINGS_REPORT_MAX_FINDINGS |
250 |
Maximum number of findings allowed in a single export |
FINDINGS_REPORT_MAX_INPUT_CHARS |
200000 |
Maximum combined text size allowed before report generation |
FINDINGS_REPORT_MAX_OUTPUT_SIZE_MB |
25 |
Maximum generated report size before the backend rejects the export |
FINDINGS_REPORT_RETENTION_DAYS |
365 |
Object-lock retention window applied to newly generated stored reports |
FINDINGS_MAILJET_API_KEY |
(empty) | Mailjet API key (empty = emails disabled) |
FINDINGS_MAILJET_API_SECRET |
(empty) | Mailjet API secret |
FINDINGS_MAILJET_FROM_EMAIL |
noreply@findings.local |
Sender email address |
FINDINGS_MAILJET_FROM_NAME |
VulnLedger |
Sender display name |
FINDINGS_APP_BASE_URL |
http://localhost |
Public URL (used in emails and redirects) |
FINDINGS_RATE_LIMIT_LOGIN |
5/minute |
Login endpoint rate limit |
FINDINGS_RATE_LIMIT_API |
60/minute |
Default API rate limit |
FINDINGS_OIDC_ENABLED |
false |
Enable OIDC SSO |
FINDINGS_OIDC_CLIENT_ID |
(empty) | OIDC client ID |
FINDINGS_OIDC_CLIENT_SECRET |
(empty) | OIDC client secret |
FINDINGS_OIDC_DISCOVERY_URL |
(empty) | OIDC discovery URL (.well-known/openid-configuration) |
FINDINGS_OIDC_REDIRECT_URI |
(empty) | OIDC callback URL |
FINDINGS_OIDC_REDIRECT_URI_ALLOWLIST |
[] |
Explicit allowlist for OIDC callback redirect URI |
FINDINGS_OIDC_REQUIRE_NONCE |
true |
Require nonce validation on OIDC callback |
FINDINGS_OIDC_DEFAULT_ROLE |
reviewer |
Default role for auto-provisioned SSO users |
FINDINGS_INITIAL_ADMIN_USERNAME |
See .env.example |
Required username for the seeded admin account |
FINDINGS_INITIAL_ADMIN_PASSWORD |
See .env.example |
Required password for the seeded admin account |
FINDINGS_INITIAL_ADMIN_EMAIL |
See .env.example |
Required email for the seeded admin account |
FINDINGS_INITIAL_ADMIN_FULL_NAME |
See .env.example |
Required display name for the seeded admin account |
FINDINGS_CLAMAV_HOST |
clamav |
ClamAV host; uploads are blocked whenever scanning is disabled or the scanner is unavailable |
FINDINGS_CLAMAV_PORT |
3310 |
ClamAV TCP port |
FINDINGS_JWT_ISSUER |
See .env.example |
Expected JWT issuer claim |
FINDINGS_JWT_AUDIENCE |
See .env.example |
Expected JWT audience claim |
FINDINGS_JWT_PRIVATE_KEY_PEM |
(empty) | RS256 private key PEM (use this or FINDINGS_JWT_PRIVATE_KEY_FILE) |
FINDINGS_JWT_PUBLIC_KEY_PEM |
(empty) | RS256 public key PEM (use this or FINDINGS_JWT_PUBLIC_KEY_FILE) |
FINDINGS_JWT_PRIVATE_KEY_FILE |
See .env.example |
Path to an RS256 private key PEM file mounted in the backend container |
FINDINGS_JWT_PUBLIC_KEY_FILE |
See .env.example |
Path to an RS256 public key PEM file mounted in the backend container |
FINDINGS_RUNTIME_MODE |
development |
Runtime mode used by backup encryption policy (production enforces secret presence) |
APP_VERSION |
0.2.2 |
Shared deployment version for frontend display (VITE_APP_VERSION) and backend metadata (FINDINGS_APP_VERSION) |
POSTGRES_PORT |
5432 |
Host port published for PostgreSQL |
SEAWEEDFS_S3_PORT |
8333 |
Host port published for the SeaweedFS S3 gateway |
BACKEND_PORT |
8000 |
Host port published for the FastAPI backend |
FRONTEND_PORT |
5173 |
Host port published for the Svelte frontend |
CLAMAV_PORT |
3310 |
Host port published for ClamAV |
CADDY_HTTP_PORT |
80 |
Host port published for Caddy HTTP |
CADDY_HTTPS_PORT |
443 |
Host port published for Caddy HTTPS and HTTP/3 |
CADDY_ATTACHMENT_MAX_SIZE |
30MB |
Reverse-proxy upload body limit; keep this at or slightly above FINDINGS_ATTACHMENT_MAX_FILE_SIZE_MB |
Sample .env for local development¶
# Required
POSTGRES_USER=change_this_db_user
POSTGRES_PASSWORD=<strong-db-password>
POSTGRES_DB=change_this_db_name
POSTGRES_HOST=localhost
POSTGRES_SERVICE_PORT=5432
FINDINGS_INITIAL_ADMIN_USERNAME=admin
FINDINGS_INITIAL_ADMIN_PASSWORD=change-this-admin-password
FINDINGS_INITIAL_ADMIN_EMAIL=admin@example.com
FINDINGS_INITIAL_ADMIN_FULL_NAME=Administrator
# SeaweedFS S3-compatible object storage
FINDINGS_OBJECT_STORAGE_ENDPOINT=localhost:8333
FINDINGS_OBJECT_STORAGE_ACCESS_KEY=findings-storage
FINDINGS_OBJECT_STORAGE_SECRET_KEY=change-this-object-storage-secret
FINDINGS_OBJECT_STORAGE_SECURE=false
FINDINGS_OBJECT_STORAGE_EVIDENCE_BUCKET=finding-attachments
FINDINGS_OBJECT_STORAGE_REPORTS_BUCKET=generated-reports
FINDINGS_REPORT_RETENTION_DAYS=365
# JWT signing (RS256)
FINDINGS_JWT_ISSUER=vulnledger-backend
FINDINGS_JWT_AUDIENCE=vulnledger-api
FINDINGS_JWT_PRIVATE_KEY_FILE=./secrets/jwt_private_key.pem
FINDINGS_JWT_PUBLIC_KEY_FILE=./secrets/jwt_public_key.pem
# Optional: Upload / report guardrails
# The backend value is the authoritative attachment limit.
# Keep the Caddy limit at or slightly above it so oversized uploads are rejected early.
FINDINGS_ATTACHMENT_MAX_FILE_SIZE_MB=25
FINDINGS_REPORT_MAX_FINDINGS=250
FINDINGS_REPORT_MAX_INPUT_CHARS=200000
FINDINGS_REPORT_MAX_OUTPUT_SIZE_MB=25
# Optional: Email notifications
FINDINGS_MAILJET_API_KEY=your-mailjet-key
FINDINGS_MAILJET_API_SECRET=your-mailjet-secret
FINDINGS_MAILJET_FROM_EMAIL=security@yourcompany.com
FINDINGS_MAILJET_FROM_NAME=VulnLedger
FINDINGS_APP_BASE_URL=http://localhost
# Optional: OIDC SSO
FINDINGS_OIDC_ENABLED=false
FINDINGS_OIDC_CLIENT_ID=
FINDINGS_OIDC_CLIENT_SECRET=
FINDINGS_OIDC_DISCOVERY_URL=
FINDINGS_OIDC_REDIRECT_URI=
# Optional: ClamAV
FINDINGS_CLAMAV_HOST=localhost
FINDINGS_CLAMAV_PORT=3310
# Optional: Reverse proxy upload cap (Caddy)
CADDY_ATTACHMENT_MAX_SIZE=30MB
# Optional: Unified app version shown in UI and backend metadata
APP_VERSION=0.2.2
JWT signing (RS256)¶
Access tokens are signed and verified with RS256 only.
- Generate an RSA key pair (keep the private key secret; 2048-bit RSA minimum):
- Load the keys into the backend using either mounted files or PEM env vars. The default Docker path is:
- Set stable issuer and audience values:
Backup configuration¶
Set on the backup service (no FINDINGS_ prefix):
| Variable | Default | Description |
|---|---|---|
BACKUP_CRON |
0 2 * * * |
Cron schedule (default: daily 2 AM) |
BACKUP_RETENTION_DAYS |
30 |
Days to keep old backups |
BACKUP_ENCRYPTION_SECRET_FILE |
/run/secrets/backup_encryption_passphrase |
Path to backup encryption passphrase file |
BACKUP_ENCRYPTION_REQUIRED |
false |
Require passphrase file for every backup; forced in production runtime mode |