@ -218,3 +218,57 @@ Each entry uses this exact structure. Omit fields marked as role-specific when t
| Next Role | none |
| Next Role | none |
---
---
### T-007 — plan — 2026-09-24T10:38:36Z
| Field | Value |
|-------|-------|
| Agent | claude |
| Summary | rework_plan T-002: diagnosed the user's local-HTTP login redirect loop as the hardcoded `Secure: true` session cookie (internal/web/app.go) being dropped by the browser over non-HTTPS origins; user chose to add an off-by-default local-testing opt-in rather than requiring a reverse proxy. Updated ROADMAP.md (new auth requirement), .ai/PLAN.md (scope, Phase 7, validation), and added task T-007 to .ai/TASKS.md as an additive follow-on to the already-shipped T-002. |
| Validation | n/a (planning only, no code changed) |
| Commit | n/a (planner does not commit) |
| Next Role | implement |
---
### T-007 — dev — 2026-09-24T10:47:05Z
| Field | Value |
|-------|-------|
| Agent | codex |
| Summary | Added an explicit local-only insecure-cookie opt-in so plain-HTTP testing can complete the login and password-change flow without changing secure defaults. |
| Validation | `go fmt ./...`, `go vet ./...`, and `go test ./...` passed in `golang:1.24`; default and opt-in Compose configuration plus `bash scripts/test-production-deployment.sh` passed. |
| Commit | `feat(auth): add local HTTP cookie opt-in` |
| Verdict | PASS |
| Blocking Findings | none |
| Next Role | human approval |
---
### T-007 — commit_task — 2026-09-24T10:48:43Z
| Field | Value |
|-------|-------|
| Agent | codex |
| Summary | Approved the reviewed T-007 local HTTP cookie opt-in and prepared its single task commit. |
| Files Changed | .ai/TASKS.md, .ai/HANDOFF.md |
| Validation | Review validation recorded in `.ai/REVIEW.md`. |
| Commit | `feat(auth): add local HTTP cookie opt-in` |
| Next Role | none |
---
### T-008 — plan — 2026-09-24T10:55:45Z
| Field | Value |
|-------|-------|
| Agent | claude |
| Summary | rework_plan T-004: user reported wanting to stream .mp4/.mkv; investigated and confirmed a real bug — `isVideoFile`/delivery `Content-Type` in internal/web/app.go rely on `mime.TypeByExtension`, which resolves nothing for video extensions inside the actual `alpine:3.21` production image (no OS MIME database, no video entries in Go's built-in fallback table), verified by inspecting the base image directly. Updated ROADMAP.md (Priority 3 note) and .ai/PLAN.md (Phase 8, validation), and added corrective task T-008 to .ai/TASKS.md as a follow-on to the already-shipped T-004, requiring an app-internal video extension/MIME table and documenting the .mkv in-browser playback caveat. |
| Validation | n/a (planning only, no code changed); bug confirmed by running `alpine:3.21` and `golang:1.24-alpine` in Docker to inspect MIME data availability and Go's builtin type table |
| T-004 | Download & video streaming: raw-byte download endpoint with correct `Content-Type`/`Content-Disposition`, full HTTP `Range` support for video files (seek/partial content/resume) with zero transcoding anywhere in the request path | done | Downloading any file returns identical bytes to the source file (checksum match); a `Range` request against a video file returns `206 Partial Content` with the correct byte range and headers; no encoding/thumbnailing/transcoding code path exists; `README.md` documents the reverse-proxy Range-header-passthrough requirement | PASS: root-confined regular-file opens; authenticated byte-identical attachment download; video `206``Range` response; unauthenticated delivery gate; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24` | none |
| T-004 | Download & video streaming: raw-byte download endpoint with correct `Content-Type`/`Content-Disposition`, full HTTP `Range` support for video files (seek/partial content/resume) with zero transcoding anywhere in the request path | done | Downloading any file returns identical bytes to the source file (checksum match); a `Range` request against a video file returns `206 Partial Content` with the correct byte range and headers; no encoding/thumbnailing/transcoding code path exists; `README.md` documents the reverse-proxy Range-header-passthrough requirement | PASS: root-confined regular-file opens; authenticated byte-identical attachment download; video `206``Range` response; unauthenticated delivery gate; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24` | none |
| T-005 | Admin user management: admin-only screens to list users, create a user (temporary password, `must_change_password=true`), reset a user's password, disable/delete a user; role-based route protection (403 for authenticated non-admins, not a redirect that leaks page existence) | done | Non-admin authenticated user gets 403 on all admin routes; admin can create a user who is then forced through the change-password gate on first login; admin can reset/disable a user; `README.md` documents the admin user-management flow | PASS: admin lifecycle/session-invalidation and HTTP role-protection/temporary-password tests; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24`; E2E not configured | none |
| T-005 | Admin user management: admin-only screens to list users, create a user (temporary password, `must_change_password=true`), reset a user's password, disable/delete a user; role-based route protection (403 for authenticated non-admins, not a redirect that leaks page existence) | done | Non-admin authenticated user gets 403 on all admin routes; admin can create a user who is then forced through the change-password gate on first login; admin can reset/disable a user; `README.md` documents the admin user-management flow | PASS: admin lifecycle/session-invalidation and HTTP role-protection/temporary-password tests; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24`; E2E not configured | none |
| T-006 | Docker packaging & deployment docs: finalize multi-stage Dockerfile (small image, no build tools in final image), finalize docker-compose example with volume mounts + root config, complete `README.md` (build/run, root config format, first-login/admin credential flow, forced password change, reverse-proxy TLS + Range passthrough requirement) | done | `docker build` produces a working image; `docker run` with a sample volume mount and root config serves the login page only until authenticated, per Priority 1; `README.md` is complete enough for a new operator to deploy without reading source code | PASS: Compose configuration validation; production image build/run, non-root runtime, and unauthenticated login boundary smoke test; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24` | none |
| T-006 | Docker packaging & deployment docs: finalize multi-stage Dockerfile (small image, no build tools in final image), finalize docker-compose example with volume mounts + root config, complete `README.md` (build/run, root config format, first-login/admin credential flow, forced password change, reverse-proxy TLS + Range passthrough requirement) | done | `docker build` produces a working image; `docker run` with a sample volume mount and root config serves the login page only until authenticated, per Priority 1; `README.md` is complete enough for a new operator to deploy without reading source code | PASS: Compose configuration validation; production image build/run, non-root runtime, and unauthenticated login boundary smoke test; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24` | none |
| T-007 | Local-testing insecure-cookie opt-in: add an off-by-default env var (e.g. `WEB_FILE_BROWSER_INSECURE_COOKIES`) that, when explicitly enabled, omits the `Secure` attribute from the session cookie so login works over plain `http://` (non-TLS, non-`localhost`-only origins) for local development; default behavior unchanged; no other session/auth behavior (`HttpOnly`, `SameSite`, expiry, forced password change) may be weakened; `README.md` gets a new "Local development over HTTP" section that explicitly warns this must never be enabled on a real/production/exposed deployment | done | With the var unset, session cookie still has `Secure` set and behavior is unchanged from T-002/T-005; with the var explicitly enabled, login over plain `http://` succeeds (no redirect loop back to `/login`), the cookie has `Secure` unset, and all other cookie/session attributes and the forced-password-change gate are unchanged; `README.md` documents local-only Docker Compose and Docker Run setup with an explicit warning | PASS: default-secure and plain-HTTP cookie-jar login/forced-change tests; Compose default/opt-in configuration validation; `go fmt ./...`, `go vet ./...`, `go test ./...` in `golang:1.24`; deployment E2E passed | none |
| T-008 | Bug fix (regression against shipped T-004): `isVideoFile` and the delivered `Content-Type` currently rely on `mime.TypeByExtension`, which resolves to nothing for `.mp4`/`.mkv`/etc. inside the actual Alpine-based production image (no OS MIME database present, no video entries in Go's built-in fallback table) — verified by inspecting `alpine:3.21` directly. Replace with an app-internal extension → MIME-type table (`.mp4`, `.m4v`, `.mov`, `.webm`, `.mkv`, `.avi`, `.mpg`/`.mpeg`, `.wmv`, `.flv` at minimum) used consistently by both the "stream" link visibility check and the file-delivery `Content-Type` header, independent of any OS/container MIME database. `README.md` documents the supported video extensions and the `.mkv`-in-browser playback caveat (download always works; native in-browser streaming of the MKV container is browser-dependent, not guaranteed, and no transcoding is performed per the project's no-encoding requirement) | ready_for_implement | Inside the actual production Docker image (not the Go build image): requesting an `.mp4` returns `Content-Type: video/mp4` and the folder listing shows a "stream" link for it; requesting an `.mkv` returns the correct Matroska `Content-Type` and a "stream" link; a `Range` request against the `.mp4` still returns `206 Partial Content` with correct byte range; unit tests cover the new extension table directly (no dependency on host/OS MIME data) so the bug can't silently regress; `README.md` lists supported video extensions and the `.mkv` browser-support caveat | n/a | implement |