From f0f8207cf73e9310e63d7db2118d1b283e035e80 Mon Sep 17 00:00:00 2001 From: Josue Zamudio Date: Wed, 19 Aug 2026 16:32:33 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20README/SPEC=20current=20=E2=80=94=20mul?= =?UTF-8?q?ti-user,=20CLI=20accounts,=20Secure=20cookie,=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: per-user history (was shared history.json), proxy/HTTPS access note, ANIWEB_SECURE, layout lists users.py + tests/ + login.html; merged duplicate history bullets - SPEC: session hardening (Secure flag, per-request account existence check), run steps include account creation + test suite, milestone 7 (auth), fixed corrupted out-of-scope tail --- README.md | 30 +++++++++++++++++------------- SPEC.md | 14 +++++++++++--- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3c452c6..4a70036 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,13 @@ uv pip install --python .venv/bin/python -r requirements.txt ./run.sh ``` -Open . `run.sh` restarts cleanly if the server -is already running; logs go to `server.log`. The server refuses to -start without at least one user (a legacy `credentials.json` -password, if any, is migrated to user `admin`). +Open it through your HTTPS proxy (or from the +host itself — browsers treat localhost as trusted). `run.sh` restarts +cleanly if the server is already running; logs go to `server.log`. +With `ANIWEB_SECURE=1` (set by `run.sh`), the session cookie is +`Secure`, so remote plain-HTTP access can't hold a session — only the +HTTPS proxy works. A legacy `credentials.json` password, if any, +is migrated to user `admin` on first start. Account management (no self-signup by design): @@ -36,24 +39,25 @@ the app or CLI are rejected immediately, even with an old cookie. - Per-user accounts (CLI-created, no self-signup): 30-day signed session cookie, 5 wrong attempts → 15-min lockout per user. PBKDF2-SHA256 hashes in `server/data/users.json` (0600). -- Per-user watch history (`server/data/history/.json`); - in-header chip with logout + delete-account. +- Per-user watch history (`server/data/history/.json`): position + saved every ~10s, resume where you left off, top-5 "recently played" + on the home page. Nothing stored in the browser — new devices pick + up your watch state after login, isolated per account. + In-header chip shows the user, with logout + delete-account. - Search with poster grid, detail view with synopsis + season tabs - HLS playback (vendored hls.js), browser ABR across 1080/720/360p - Dub/sub toggle - Autoplays the first episode (or resumes the last watched one); if the browser blocks autoplay, a "click to play" overlay appears -- Server-persisted watch history (`server/data/history.json`): watch - position saved every ~10s, resume where you left off, top-5 "recently - played" on the home page — shared across browsers and devices ## Layout ``` -server/ FastAPI app (app.py) + anidb.app fetchers (upstream.py) - upstream.py is the ONLY Cloudflare-facing code -static/ front-end: index.html, app.js, style.css, hls.min.js (vendored) -run.sh launch/restart the server +server/ FastAPI app (app.py), anidb.app fetchers (upstream.py), + accounts (users.py). upstream.py is the ONLY Cloudflare-facing code +static/ front-end: index.html, login.html, app.js, style.css, hls.min.js (vendored) +tests/ test suite (run.py orchestrator, HTTP + browser tests) +run.sh launch/restart the server (sets ANIWEB_SECURE=1) SPEC.md design + the verified anidb.app data flow ``` diff --git a/SPEC.md b/SPEC.md index 7250df8..880c162 100644 --- a/SPEC.md +++ b/SPEC.md @@ -66,6 +66,10 @@ Two moving parts, no database, no framework on the frontend. (`python -m server.users add|rm|list`) — no self-signup. One-time migration: legacy single-password `credentials.json` → user `admin`; `history.json` → `history/admin.json`. +- Session hardening: cookies are `HttpOnly`, `SameSite=Lax`, and + `Secure` when `ANIWEB_SECURE=1` (set by `run.sh`); the middleware + re-verifies the account still exists per request, so deleted + accounts' cookies are rejected immediately. - `GET /api/me`, `POST /account/delete` → current user / self-delete (account + history file). @@ -91,17 +95,19 @@ Flow: search box → results grid (poster + title, click) → detail view ```sh uv venv .venv && uv pip install --python .venv/bin/python -r requirements.txt -./run.sh # launch/restart, logs to server.log +.venv/bin/python -m server.users add # one-time account creation +./run.sh # launch/restart, logs to server.log, sets ANIWEB_SECURE=1 ``` -Open `http://localhost:8321` (bound to 0.0.0.0 for LAN access). +Use the app through your HTTPS proxy (with `Secure` cookies, plain +remote HTTP can't hold a session). `python tests/run.py` runs the +full suite against a throwaway server on :8399. ## Out of scope (v1) Downloads (`-d`), next-episode countdown (`-N`), syncplay, intro skip, ani-schedule integration, episode titles (not in anidb's API — needs a MyAnimeList API key). Add when someone asks. -anidb's API — needs a MyAnimeList API key). Add when someone asks. ## Risks @@ -122,3 +128,5 @@ anidb's API — needs a MyAnimeList API key). Add when someone asks. 5. UI — search → detail → episodes → player. 6. Polish — history with position + resume, sub/dub toggle, error states, autoplay default, `run.sh`. +7. Auth — login screen, then multi-user CLI-created accounts with + per-user history + test suite.