Files
ledgerdock/doc/operations-and-configuration.md

170 lines
8.3 KiB
Markdown

# Operations And Configuration
## Runtime Services
`docker-compose.yml` defines:
- `db` (Postgres 16)
- `redis` (Redis 7)
- `typesense` (Typesense 29)
- `api` (FastAPI backend)
- `worker` (RQ worker via `python -m app.worker.run_worker`)
- `frontend` (Vite React UI)
Persistent host bind mounts (default root `./data`, overridable with `DCM_DATA_DIR`):
- `${DCM_DATA_DIR:-./data}/db-data`
- `${DCM_DATA_DIR:-./data}/redis-data`
- `${DCM_DATA_DIR:-./data}/storage`
- `${DCM_DATA_DIR:-./data}/typesense-data`
Reset all persisted runtime data:
```bash
docker compose down
rm -rf ${DCM_DATA_DIR:-./data}
```
## Core Commands
Start or rebuild:
```bash
docker compose up --build -d
```
Stop:
```bash
docker compose down
```
Tail logs:
```bash
docker compose logs -f
```
## Host Bind Mounts
Compose is configured with host bind mounts for persistent data. Ensure host directories exist and are writable by the backend runtime user.
Backend and worker run as non-root user `uid=10001` inside containers. For host-mounted storage paths:
```bash
mkdir -p ${DCM_DATA_DIR:-./data}/storage
sudo chown -R 10001:10001 ${DCM_DATA_DIR:-./data}/storage
sudo chmod -R u+rwX,g+rwX ${DCM_DATA_DIR:-./data}/storage
```
If permissions are incorrect, API startup fails with errors similar to:
- `PermissionError: [Errno 13] Permission denied: '/data/storage'`
- `FileNotFoundError` for `/data/storage/originals`
## Frontend Build Baseline
The frontend Dockerfile uses `node:22-slim` with a standard `npm ci --no-audit` install step and no npm-specific build tuning flags.
## Authentication Model
- Legacy shared build-time frontend token behavior was removed.
- API now uses server-issued sessions that are stored in HttpOnly cookies (`dcm_session`) with a separate CSRF cookie (`dcm_csrf`).
- Bootstrap users are provisioned from environment:
- `AUTH_BOOTSTRAP_ADMIN_USERNAME`
- `AUTH_BOOTSTRAP_ADMIN_PASSWORD`
- optional `AUTH_BOOTSTRAP_USER_USERNAME`
- optional `AUTH_BOOTSTRAP_USER_PASSWORD`
- Login brute-force protection is enabled by default and keyed by username and source IP:
- `AUTH_LOGIN_FAILURE_LIMIT`
- `AUTH_LOGIN_FAILURE_WINDOW_SECONDS`
- `AUTH_LOGIN_LOCKOUT_BASE_SECONDS`
- `AUTH_LOGIN_LOCKOUT_MAX_SECONDS`
- Frontend signs in through `/api/v1/auth/login` and relies on browser session persistence for valid cookie-backed sessions.
## DEV And LIVE Configuration Matrix
Use `.env.example` as baseline. The table below documents user-managed settings and recommended values.
| Variable | Local DEV (HTTP, docker-only) | LIVE (HTTPS behind reverse proxy) |
| --- | --- | --- |
| `APP_ENV` | `development` | `production` |
| `HOST_BIND_IP` | `127.0.0.1` or local LAN bind if needed | `127.0.0.1` (publish behind proxy only) |
| `PUBLIC_BASE_URL` | `http://localhost:8000` | `https://api.example.com` |
| `VITE_API_BASE` | empty for host-derived `http://<frontend-host>:8000/api/v1`, or explicit local URL | `https://api.example.com/api/v1` (build-time value for production frontend image) |
| `VITE_ALLOWED_HOSTS` | optional comma-separated hostnames, for example `localhost,docs.lan` | optional comma-separated public frontend hostnames, for example `app.example.com` |
| `CORS_ORIGINS` | `["http://localhost:5173","http://localhost:3000"]` | exact frontend origins only, for example `["https://app.example.com"]` |
| `REDIS_URL` | `redis://:<password>@redis:6379/0` in isolated local network | `rediss://:<password>@redis.internal:6379/0` |
| `REDIS_SECURITY_MODE` | `compat` or `auto` | `strict` |
| `REDIS_TLS_MODE` | `allow_insecure` or `auto` | `required` |
| `AUTH_LOGIN_FAILURE_LIMIT` | default `5` | tune to identity-protection policy and support requirements |
| `AUTH_LOGIN_FAILURE_WINDOW_SECONDS` | default `900` | tune to identity-protection policy and support requirements |
| `AUTH_LOGIN_LOCKOUT_BASE_SECONDS` | default `30` | tune to identity-protection policy and support requirements |
| `AUTH_LOGIN_LOCKOUT_MAX_SECONDS` | default `900` | tune to identity-protection policy and support requirements |
| `AUTH_COOKIE_DOMAIN` | empty (host-only cookies) | parent frontend/API domain for split hosts, for example `docs.lan` |
| `AUTH_COOKIE_SAMESITE` | `auto` | `none` for cross-origin frontend/API deployments, `lax` or `strict` for same-origin |
| `PROVIDER_BASE_URL_ALLOW_HTTP` | `true` only when intentionally testing local HTTP provider endpoints | `false` |
| `PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK` | `true` only for trusted local development targets | `false` |
| `PROVIDER_BASE_URL_ALLOWLIST` | allow needed test hosts | explicit production allowlist, for example `["api.openai.com"]` |
| `PROCESSING_LOG_STORE_MODEL_IO_TEXT` | `false` by default; temporary `true` only for controlled debugging | `false` |
| `PROCESSING_LOG_STORE_PAYLOAD_TEXT` | `false` by default; temporary `true` only for controlled debugging | `false` |
| `CONTENT_EXPORT_MAX_DOCUMENTS` | default `250` or lower based on host memory | tuned to production capacity |
| `CONTENT_EXPORT_MAX_TOTAL_BYTES` | default `52428800` (50 MiB) or lower | tuned to production capacity |
| `CONTENT_EXPORT_RATE_LIMIT_PER_MINUTE` | default `6` | tuned to API throughput and abuse model |
`PUBLIC_BASE_URL` must point to the backend API public URL, not the frontend URL.
## HTTPS Proxy Deployment Notes
This application supports both:
- local HTTP-only operation (no TLS termination in containers)
- HTTPS deployment behind a reverse proxy that handles TLS
Recommended LIVE pattern:
1. Proxy terminates TLS and forwards to `api` and `frontend` internal HTTP endpoints.
2. Keep container published ports bound to localhost or internal network.
3. Set `PUBLIC_BASE_URL` and `VITE_API_BASE` to final HTTPS URLs.
4. Set `CORS_ORIGINS` to exact HTTPS frontend origins.
5. Credentialed CORS is enabled and constrained for cookie-based sessions with strict origin allowlists.
## Security Controls
- CORS uses explicit origin allowlist only; broad origin regex matching is removed.
- Worker Redis startup validates URL auth and TLS policy before consuming jobs.
- Provider API keys are encrypted at rest with standard AEAD (`cryptography` Fernet).
- legacy `enc-v1` payloads are read for backward compatibility
- new writes use `enc-v2`
- Processing logs default to metadata-only persistence.
- Login endpoint applies escalating temporary lockout on repeated failed credentials using Redis-backed subject keys for username and source IP.
- Markdown export enforces:
- max document count
- max total markdown bytes
- per-user Redis-backed rate limit
- spool-file streaming to avoid unbounded memory archives
- User-role document access is owner-scoped for non-admin accounts.
## Frontend Runtime
- Frontend no longer consumes `VITE_API_TOKEN`.
- Frontend image target is environment-driven:
- `APP_ENV=development` builds the `development` target and runs Vite dev server
- `APP_ENV=production` builds the `production` target and serves static assets through unprivileged Nginx
- Frontend Docker targets are selected from `APP_ENV`, so use `development` or `production` values.
- Production frontend Nginx uses non-root runtime plus `/tmp` temp-path configuration so it can run with container capability dropping enabled.
- Vite dev server host allowlist uses the union of:
- hostnames extracted from `CORS_ORIGINS`
- optional explicit hostnames from `VITE_ALLOWED_HOSTS`
- `VITE_ALLOWED_HOSTS` only affects development mode where Vite is running.
- API auth cookies support optional domain and SameSite configuration through `AUTH_COOKIE_DOMAIN` and `AUTH_COOKIE_SAMESITE`.
- HTTPS cookie security detection falls back to `PUBLIC_BASE_URL` scheme when proxy headers are missing.
- CSRF validation accepts header matches against any `dcm_csrf` cookie value in the request, covering stale plus fresh duplicate-cookie transitions.
- Session authentication is cookie-based; browser reloads and new tabs can reuse an active session until it expires or is revoked.
- Protected media and file download flows still use authenticated fetch plus blob/object URL handling.
## Validation Checklist
After configuration changes:
- `GET /api/v1/health` returns healthy response
- login succeeds for bootstrap admin user
- admin can upload, search, open preview, download, and export markdown
- user account can only access its own documents
- admin-only settings and processing logs are not accessible by user role
- `docker compose logs -f api worker` shows no startup validation failures