You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7.4 KiB
7.4 KiB
Plan
Status: active
Goal: implement the scope defined in ROADMAP.md — a small, login-gated,
container-based file browser that lets an admin mount arbitrary host folders,
browse them in a light/dark web UI, and download or stream video files with
zero server-side transcoding.
Scope
- Go backend, SQLite for users/sessions, server-rendered HTML templates + minimal vanilla JS, single Docker image, plain HTTP behind a reverse proxy.
- Zero unauthenticated access to any route except the login page: login is
gated first, then a mandatory password-change gate for any account with
must_change_password=true(including the seeded admin), then file browsing/download/streaming for bothadminanduserroles. - Multiple named roots are Docker volume mounts declared in a config file/env var (container path + display name), read at startup; adding a root requires a config edit + container restart (documented limitation, not solved in-app).
- Read-only browsing: list folders/files, breadcrumb navigation, strict containment inside each root (no traversal outside the mount, no symlink escape).
- Download and video streaming serve raw bytes with full HTTP
Rangesupport; no transcoding, thumbnailing, or format conversion anywhere. - Admin-only user management: create/list/reset-password/disable users; non-admin gets 403 on admin routes.
- Documentation:
README.mdis kept current with every behavior/interface/ workflow/config change in the same commit that makes the change (no follow-up doc tasks). - Local-testing-only opt-in to disable the
Securesession-cookie attribute (default: secure/on), so login works over plainhttp://for local development without a reverse proxy.
Out of scope (this cycle)
- Per-user/per-root permission restrictions.
- Any write operations from the UI (upload/rename/delete/move).
- Cross-folder search.
- App-managed TLS (assumed reverse proxy).
Acceptance Criteria
- No file listing, file content, download, stream, or admin route is ever reachable without a valid authenticated session — verified by an explicit test hitting each such route unauthenticated and expecting a redirect/401, never data.
- Login with the seeded admin (
boss@zecevic-ninja.com/Start1234!) forces a password change before any other page is reachable, and this same forced-change gate applies to any user created later with a temporary password. - Both
adminanduserroles can browse, download, and stream all configured roots; onlyadmincan reach user-management screens. - Downloaded file bytes are byte-identical to the source file; video
Rangerequests return206 Partial Contentwith correct byte ranges, enabling native browser seeking with no server-side transcoding. - Path handling cannot escape a configured root via
../traversal or symlink. docker build+docker run(with a sample volume mount and root config) produces a working, login-gated instance, andREADME.mdis sufficient for an operator to deploy it without reading source.
Implementation Phases
Phase 1 — Scaffolding & config (T-001)
- Go module layout, root-config loading (container path + display name from file/env), Dockerfile skeleton, docker-compose example, README skeleton.
Phase 2 — Data & auth foundation (T-002)
- SQLite schema/migrations for users + sessions, bcrypt hashing, seed admin
with
must_change_password=true, session-cookie middleware, login/logout, forced password-change gate blocking all other routes.
Phase 3 — Folder browsing UI (T-003)
- Root listing, directory navigation with breadcrumbs, strict path containment, light/dark theme, minimal vanilla JS/CSS.
Phase 4 — Download & video streaming (T-004)
- Raw-byte download endpoint, full HTTP Range support for video seek/resume, zero transcoding.
Phase 5 — Admin user management (T-005)
- Admin-only list/create/reset/disable-user screens, role-based route protection (403 for non-admin).
Phase 6 — Packaging & docs finalization (T-006)
- Finalize multi-stage Dockerfile and docker-compose example, complete
README.mdend to end (build/run, root config format, first-login/admin credential flow, forced password change, reverse-proxy TLS + Range passthrough requirement).
Phase 7 — Local-testing insecure-cookie opt-in (T-007)
- Added after T-001..T-006 shipped: an explicit, off-by-default env var
that disables the
Secureattribute on the session cookie so login works over plainhttp://for local development (e.g. non-https://access, including non-localhostorigins where the browser doesn't treat the origin as a secure context). Default behavior (Secure cookie) is unchanged for anyone who doesn't set the var.README.mdgets a new "Local development over HTTP" section spelling out that this switch must never be enabled on a real/production/exposed deployment.
Phase 8 — Fix video type detection for real containers (T-008)
- Bug fix against the shipped T-004 behavior:
isVideoFileand theContent-Typeserved for downloads/streams currently rely onmime.TypeByExtension, which returns nothing useful for.mp4/.mkv/etc. inside the actual Alpine-based runtime image (no OS MIME database present, and Go's built-in fallback table has no video entries). Replace this with an app-internal extension → MIME-type table covering at least.mp4,.m4v,.mov,.webm,.mkv,.avi,.mpg/.mpeg,.wmv,.flv, used consistently for both the "stream" link visibility check and theContent-Typeheader on file delivery, independent of the OS/container MIME database.README.mddocuments the supported video extensions and the.mkv-in-browser caveat (downloads always work; native in-browser playback of the MKV container is browser-dependent and not guaranteed, since no transcoding is performed per the project's no-encoding requirement).
Tasks are sequential (T-001 → T-006); T-007 and T-008 are additive
follow-on tasks built on top of the already-shipped T-002 and T-004 work,
respectively. Each phase's acceptance criteria and required documentation
updates are tracked per-task in .ai/TASKS.md.
Validation
Run before finishing each task, broader pass before commit:
go fmt ./...go vet ./...go test ./...
Task-specific manual/E2E checks (documented per task in .ai/TASKS.md):
- Unauthenticated access attempts against every protected route.
- Forced password-change flow for the seeded admin and for a newly created user.
- Path-traversal / symlink-escape attempts against the browsing routes.
- Checksum comparison of a downloaded file against its source.
Range-header request against a video file, confirming206 Partial Contentand correct byte range.docker build+docker runsmoke test with a sample mounted folder.- With the insecure-cookie opt-in unset (default), confirm the session
cookie still has
Secureset. With it explicitly enabled, confirm login over plainhttp://succeeds and the cookie no longer hasSecureset, whileHttpOnly/SameSite/expiry/forced-password-change behavior is unchanged. - Inside the actual production Docker image (not the
golang:*build image, which does have a fuller MIME setup) — request an.mp4file and confirmContent-Type: video/mp4and a visible "stream" link; same for.mkvwith the correct MatroskaContent-Type; confirm aRangerequest against the.mp4still returns206 Partial Content.