# Document Access Service · Deployment notes · Batch 13 **Scope:** internal/team deployment candidate. NOT internet-facing yet. ## Environment variables | Var | Default | Effect | |-----|---------|--------| | `DAS_HOST` | `127.0.0.1` | bind host | | `DAS_PORT` | `8090` | bind port | | `DAS_USER_STORE` | `./user_store_examples.json` | roster path | | `DAS_VISIBILITY_MATRIX` | `../../assets/access/document_visibility_matrix.json` | group→doc map | | `DAS_SIGNING_KEY` | (file-backed) | HMAC key; when set, overrides file | | `DAS_SESSION_TTL` | `86400` | token lifetime in seconds | | `DAS_TEAM_SECRET` | `` (empty = disabled) | when set, login must submit matching `team_secret` | | `DAS_REJECT_QUERY_TOKEN` | `false` | when `true`, tokens accepted only via cookie or `Authorization: Bearer` | | `DAS_LOGIN_RATE_LIMIT` | `5` | logins per minute per IP; set `0` to disable | | `DAS_ALLOW_ORIGINS` | `*` | CORS allow-origins; comma-separated. For team deployment, set explicit origins | | `DAS_DOCS_ROOT` | `../..` (repo docs root) | root directory the render endpoint reads HTML from | | `DAS_LOG_LEVEL` | `INFO` | log level | ## Signing key resolution (Batch 13) 1. `DAS_SIGNING_KEY` env var (operator-controlled, preferred for team deployment) 2. `./das-signing-key.local` file (auto-generated on first run, 0600 perms, persistent across restarts) 3. ephemeral random key (fallback if file write fails — dev only) **Important:** The file `das-signing-key.local` must be gitignored. Never commit a real signing key. ## Team-usable quick start ```bash # 1. Pick a shared team secret + signing key (store in a secret manager ideally) export DAS_TEAM_SECRET="team-secret-rotate-me" export DAS_SIGNING_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')" export DAS_SESSION_TTL=28800 # 8h export DAS_ALLOW_ORIGINS="https://console.pattayatogether.com" export DAS_REJECT_QUERY_TOKEN=true # 2. Run bash run.sh ``` Team members log in by POSTing: ```bash curl -sS -c /tmp/c -X POST http://:8090/api/access/login \ -H 'Content-Type: application/json' \ -d '{"email":"admin@pattayatogether.example","team_secret":"team-secret-rotate-me"}' ``` ## Endpoints (Batch 13) | Method | Path | Purpose | |--------|------|---------| | GET | `/api/access/health` | liveness + config snapshot | | POST | `/api/access/login` | email (+ team_secret if enforced) → session | | POST | `/api/access/logout` | revoke | | GET | `/api/access/me` | current profile | | GET | `/api/access/resolve?doc_id=…` | per-doc decision | | GET | `/api/access/gate?doc_id=…&include_stub=…` | envelope + optional stub HTML | | GET | `/api/access/render?doc_id=…` | **Batch 13 · server-delivered HTML**; visible → full file; restricted → file with banner; blocked → stub | | GET | `/api/access/groups` | per-user group visibility | | GET | `/api/access/documents[?group_id=…]` | filtered list | | GET | `/api/access/debug/user-store` | dev dump | ## Render endpoint (Batch 13) The render endpoint reads the target HTML file from disk (`DAS_DOCS_ROOT` + `doc_id`) and returns: - **visible** → the full file, headers `X-DAS-Render-State: visible` - **restricted** → file + sticky banner inserted at ``, headers `X-DAS-Render-State: restricted` - **hidden-doc / hidden-group / not-granted** → inline stub HTML (no file contents returned), headers `X-DAS-Render-State: ` Security: - `doc_id` is validated to stay under `DAS_DOCS_ROOT` · rejects `..`, absolute paths, `:` - Only `.html` files accepted - `Cache-Control: private, no-store` on every response **Limitation:** the static HTML file at its original URL is still served by whatever host serves the files. The render endpoint provides a server-gated variant. A reverse-proxy rule is still required to route all traffic through `/api/access/render` for true route-level protection — that's a platform/deployment decision, out of scope for this service. ## Known limits - No TLS. Cookie is `HttpOnly=false`, `Secure=false` so the shell JS can read it cross-surface in dev. - In-memory rate-limit bucket (single-process). For multi-instance, use Redis. - Revocation map is in-process. Restart invalidates all tokens unless `DAS_SIGNING_KEY` is persistent. - No password authentication. Login is email (optionally + team secret). - No audit log of auth events. ## Rotation - To force logout everyone: delete `das-signing-key.local` and restart. - To rotate team secret: update `DAS_TEAM_SECRET` and restart. Active sessions remain (they were issued before rotation) unless also combined with signing-key rotation.