# Document Access Service · Batch 8 → 12 · local/dev auth **Batch 12 update**: sessions are now HMAC-SHA256 signed with explicit exp; logout revokes by signature; disabled users are rejected; new `/api/access/gate` endpoint returns a render envelope + optional stub HTML consumed by the shell. **A-owned · session_a** A FastAPI backend that issues opaque session tokens, resolves per-document visibility against a file-backed user store, and exposes the same decision contract the Universal Document Shell (`docs/assets/document-shell.js`) uses when the backend is reachable. ## Run ```bash bash run.sh ``` Defaults to `http://127.0.0.1:8090`. Environment overrides: | Variable | Default | Purpose | |----------|---------|---------| | `DAS_HOST` | `127.0.0.1` | Bind host | | `DAS_PORT` | `8090` | Bind port | | `DAS_USER_STORE` | `./user_store_examples.json` | User roster path | | `DAS_VISIBILITY_MATRIX` | `../../assets/access/document_visibility_matrix.json` | Group→doc map path | ## Honest state - **Local/dev only.** Do NOT deploy as-is. - No password hashing. Any request carrying a known email is authenticated. - Tokens are UUID v4, stored in-process (lost on restart). - No TLS. CORS is open for dev. - No audit trail, no rate limiting. ## Endpoints | Method | Path | Purpose | |--------|------|---------| | GET | `/api/access/health` | Liveness + user-store stats | | POST | `/api/access/login` | `{email}` → token + profile + cookie | | POST | `/api/access/logout` | Invalidate token | | GET | `/api/access/me` | Current profile (anonymous fallback) | | GET | `/api/access/resolve?doc_id=…` | Visibility + share/export policy for a doc | | GET | `/api/access/groups` | Group visibility map for current user | | GET | `/api/access/documents[?group_id=…]` | Filtered doc list (hidden rows omitted) | | GET | `/api/access/debug/user-store` | Dev-only dump | ## Example sessions ```bash # Admin · full access curl -c /tmp/c -X POST http://127.0.0.1:8090/api/access/login \ -H 'Content-Type: application/json' \ -d '{"email":"admin@pattayatogether.example"}' curl -b /tmp/c http://127.0.0.1:8090/api/access/me curl -b /tmp/c "http://127.0.0.1:8090/api/access/resolve?doc_id=/planning/feature-flags-batch-4-readiness.html" # External partner · sees restricted banner on that doc curl -c /tmp/c2 -X POST http://127.0.0.1:8090/api/access/login \ -H 'Content-Type: application/json' \ -d '{"email":"partner@external.example"}' curl -b /tmp/c2 "http://127.0.0.1:8090/api/access/resolve?doc_id=/kb/phase-overview-1a-12f.html" ``` ## Shell integration `docs/assets/document-shell.js` tries, in order: 1. `GET /api/access/me` (env-configurable via `window.DS_AUTH_BASE` or `data-ds-auth-base` attribute on ``) 2. If 200 and `authenticated=true` → use backend profile 3. If unreachable → fall back to browser-local profile with a visible "OFFLINE · local fallback" badge in the user panel Either way, the shell uses the same profile shape. ## Contracts - `session_contract.json` — session shape + endpoints - `access_contract.json` — resolution algorithm + policy - `access_decision_examples.json` — 10 golden decision outcomes - `user_store_examples.json` — seed user roster ## Non-goals (deferred) - Password / passkey authentication - OAuth / SSO - Token signing (JWT RS256) - Rate limiting - Audit sink delivery - Policy inheritance / wildcards - Production deployment hardening