le-app/directus/deployment.md

55 lines
5.1 KiB
Markdown
Raw Permalink Normal View History

# Production deployment
Unless marked **inferred** or **unresolved**, facts below are verified by `reference/deployment-inventory.md`, `reference/docker-compose.sanitized.yml`, `reference/environment-variable-names.txt`, `reference/nginx-summary.txt`, or `reference/exports/settings.json`.
## Runtime and topology
| Item | Production observation |
| --- | --- |
| Directus version | 12.1.1 |
| Container image | `directus/directus:latest` |
| Compose service and container | `directus` |
| Restart policy | `unless-stopped` |
| Database client | MySQL; host, database, and credential values were intentionally omitted |
| Public URL | `https://forms.lasereverything.net` |
| Container port | Directus listens on 8055; Compose binds it to host loopback `127.0.0.1:8056` |
| Networks | Private `directus_net` bridge with IPv4/IPv6 subnets, plus external `backend_net` |
| Upload mount | Host `./uploads` to `/directus/uploads`, read/write |
| Extension mount | Host `./extensions` to `/directus/extensions`, read/write |
Production currently uses the mutable `directus/directus:latest` image tag. This document records that fact and does not change it. The running version was separately observed as 12.1.1, so a future container recreation could select a different image unless deployment controls outside the supplied evidence pin it.
The extensions directory is mounted, but its contents were not exported. Whether custom extensions are installed or active is **unresolved**.
## Reverse proxy
Nginx terminates TLS for `forms.lasereverything.net` and proxies to host loopback port 8056. It forwards the original host, client IP, forwarding chain, scheme, and WebSocket upgrade headers. Proxy read and send timeouts are 300 seconds. The request body limit is 100 MB. TLS 1.2 and 1.3 are enabled and HSTS is emitted for one year with subdomains.
The sanitized Nginx summary shows reflected-origin CORS response headers with credentials enabled and permits the common API methods and request headers. Directus-specific CORS environment variables are not explicitly set. The exact Nginx origin allow-list or surrounding condition that governs the reflected origin was not included, so whether arbitrary origins are accepted is **unresolved**.
## Mail
Directus is explicitly configured for SMTP transport. The observed categories are sender, SMTP host, port, secure mode, username, and password. Port 465 and secure mode `true` are verified; sender, host, and credential values were omitted. Delivery behavior was not tested.
The Next.js application separately references its own SMTP variable names for supporter-claim mail. Those names are documented in `env.example`; whether they point to the same SMTP service is **unresolved**.
## Authentication and security configuration
| Area | Explicit production evidence | Default or unresolved behavior |
| --- | --- | --- |
| Bootstrap administrator | `ADMIN_EMAIL` and `ADMIN_PASSWORD` names are present | Values and whether bootstrap variables still affect an initialized database are intentionally unknown |
| Signing/encryption material | `KEY` and `SECRET` names are present | Values omitted |
| Public registration | No registration environment variables are explicit; settings export has `public_registration=0` | Public `/users/register` is disabled; the app instead uses a privileged `/users` create flow |
| Registration verification | Settings export has `public_registration_verify_email=0` and no public registration role/filter | Inactive while public registration is disabled |
| Auth providers | `AUTH_PROVIDERS` is not explicitly set | Available/default providers were not exported; application code assumes local email/password auth |
| Login-attempt setting | Column layout reports a database default of 25 | The live `auth_login_attempts` value was not exported; the app also applies an in-memory 20-per-minute IP limit |
| Password policy | Column exists and defaults to null in the exported table layout | The live setting was not exported; the app enforces only an eight-character minimum on registration/change |
| CORS | No Directus CORS environment variables are explicit | Nginx adds CORS headers; effective origin policy is unresolved as noted above |
| Cookies | No Directus cookie environment variables are explicit | The app stores Directus tokens in its own `ma_at` and `ma_rt` cookies; effective Directus defaults are not proven here |
| TFA | All exported policies have `enforce_tfa=0` | Per-user TFA state was intentionally not exported |
| Policy IP restrictions | All exported policies have `ip_access=null` | No policy-level IP restriction is evidenced |
| Admin/app access | Administrator policy has both; all other exported policies have neither | Verified from policy export |
| MCP/AI/new 12.x settings | System-table columns exist | Live values were not included in the sanitized settings export and must not be inferred from column defaults |
Environment absence proves only that a setting was not explicitly supplied to the collected container. It does not prove behavior controlled by the database, an extension, another configuration source, or a Directus default that may vary by version.