aboutsummaryrefslogtreecommitdiff
path: root/DESIGN.md
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-02-13 08:45:49 +0100
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-02-13 08:45:49 +0100
commit17be2fcb106ddc9c36628e9f5dece28369affc30 (patch)
tree4f3cb1d2fade5467f6c79a9a1abe1da0852a61d0 /DESIGN.md
Extract gallery app from perso/vps into standalone project
Extracted all gallery-related files from ~/perso/vps: - gallery.go, gallery_test.go (Go source + tests) - templates/ (index, login, gallery HTML) - static/style.css - go.mod/go.sum (renamed module to github.com/kagamino/kagapics) - Caddyfile.example (gallery-only Caddy config) - DESIGN.md (from galery.md)
Diffstat (limited to 'DESIGN.md')
-rw-r--r--DESIGN.md277
1 files changed, 277 insertions, 0 deletions
diff --git a/DESIGN.md b/DESIGN.md
new file mode 100644
index 0000000..39f7db4
--- /dev/null
+++ b/DESIGN.md
@@ -0,0 +1,277 @@
+# Photo Gallery — Design Document
+
+## 0. Solution
+
+**Go app (Option B)** — a single static binary reverse-proxied by Caddy.
+
+- Caddy handles TLS and serves the app on a secret URL path (e.g., `https://photos.example.com/trip-crete-2026-a8f3b2c1/`).
+- The Go binary serves an HTML page with multi-file upload (drag-and-drop), a file listing, and a "download all as zip" button.
+- Photos land in a configurable directory (one per trip). When the trip is done, stop the binary, move the folder to cold storage.
+- No database, no runtime deps, no Docker. ~200-400 lines of Go, ~10 MB binary.
+
+**Why this over pure Caddy (Option A):** At this scale (10 friends, 200 photos, ~4 GB max), both work. But the Go app gives a noticeably better mobile upload experience (multi-file, progress feedback) and zip download for free — worth the cost of one extra process.
+
+---
+
+## 1. Context & Scope
+
+After a holiday with friends, everyone has photos scattered across their phones. The goal is a self-hosted, lightweight web application where:
+
+- Friends open a link in their browser, upload their photos.
+- Friends can browse and download photos others have uploaded.
+- Once everyone has contributed, all photos live on the server — a single consolidated archive.
+- The host (you) keeps everything on their own infrastructure. No third-party cloud involved.
+
+**Current landscape:** Caddy is already used as the primary web server/reverse proxy. The solution should integrate naturally with it. The server is personal infrastructure — simplicity and low resource usage matter more than scaling to thousands of users.
+
+---
+
+## 2. Goals & Non-Goals
+
+### Goals
+
+- **Browser-only UX** — no app install, no client software, no account creation.
+- **Upload & download** — friends can push photos and pull the full set.
+- **Lightweight** — minimal dependencies, low memory/CPU footprint, easy to deploy and tear down.
+- **Self-hosted** — all data stays on your server, full ownership.
+- **Simple ops** — ideally a single binary or a Caddyfile tweak, no database.
+- **Caddy-friendly** — works behind or with Caddy with minimal config.
+
+### Non-Goals
+
+- User accounts or authentication beyond a shared secret/link.
+- Image editing, resizing, or transcoding.
+- Long-lived public gallery (this is ephemeral, per-trip).
+- Mobile app.
+- Handling thousands of concurrent users.
+- Video support (nice-to-have at best, not a driver).
+
+---
+
+## 3. Design
+
+The system is intentionally shallow. One server, one directory of files, one web interface.
+
+```mermaid
+graph LR
+ subgraph Friends
+ A[Browser]
+ B[Browser]
+ C[Browser]
+ end
+
+ subgraph Server
+ D[Caddy]
+ E[Application / File Backend]
+ F[/photos directory/]
+ end
+
+ A -- "upload/download via HTTPS" --> D
+ B -- "upload/download via HTTPS" --> D
+ C -- "upload/download via HTTPS" --> D
+ D -- "reverse proxy or direct serve" --> E
+ E -- "read/write" --> F
+```
+
+### Features
+
+#### Friends (end users)
+
+| Feature | Detail |
+|---|---|
+| **Batch upload** | Select multiple photos at once (file picker or drag-and-drop) and upload them in a single action. Progress feedback per file. |
+| **Browse gallery** | View all uploaded photos directly in the browser — no download required. Images are served as compressed/resized previews to keep page load fast. |
+| **Download as zip** | Download the entire directory as a single `.zip` file with one click. |
+| **Password access** | Enter a directory-specific password to access the gallery. No account creation. |
+
+#### Operator (you)
+
+| Feature | Detail |
+|---|---|
+| **Create directory + password** | Create a new photo directory (one per trip/event) and assign it a password. This is the only admin action needed before sharing the link with friends. |
+| **Tear down** | When collection is done, stop the service or remove the route. Archive the photos directory to cold storage. |
+
+### Key design points
+
+- **One directory per event** — each trip/event gets its own directory and its own password. No shared state between directories.
+- **Caddy terminates TLS** — every approach sits behind Caddy for HTTPS + domain.
+- **Access control** — password per directory, set by the operator at creation time. Friends enter the password once to access upload/browse/download.
+- **Ephemeral lifecycle** — spin up before sharing the link, tear down after collection is done. The photos directory persists.
+- **Compressed previews** — the gallery view serves resized/compressed versions of photos for fast browsing. Original full-resolution files are preserved on disk and served in the zip download.
+
+---
+
+## 4. Alternatives
+
+### A. Caddy `file_server` + `upload` module (pure Caddy)
+
+Use Caddy's built-in `file_server` for browsing/downloading, combined with a community Caddy upload handler plugin ([caddyserver/caddy-upload](https://github.com/caddyserver/caddy-upload) or similar).
+
+| Aspect | Detail |
+|---|---|
+| **How it works** | Caddy serves the directory with `browse`. Uploads go through a Caddy handler that accepts `PUT`/`POST` and writes to disk. A minimal HTML page with a `<form>` or JS fetch provides the upload UI. |
+| **Dependencies** | Caddy + one plugin (or a custom build via `xcaddy`). |
+| **Upload UX** | Requires a small static HTML page with a drag-and-drop or file picker form. No server-side code beyond Caddy. |
+| **Download UX** | Caddy's built-in browse UI lists files; click to download individually. Bulk download would need an external script or zip-on-the-fly. |
+
+**Pros:**
+- Zero extra processes — it's just Caddy.
+- Already in your stack; nothing new to learn operationally.
+- TLS, basic auth, rate-limiting all native.
+
+**Cons:**
+- Upload plugin maturity varies; may need `xcaddy build` to include it.
+- Browse UI is functional but bare — no thumbnails, no gallery feel.
+- No bulk zip download out of the box.
+- Multi-file upload from a phone browser can be clunky without a custom HTML page.
+
+---
+
+### B. Minimal Go application (custom binary)
+
+A small, purpose-built Go HTTP server: one binary, no dependencies, serves an HTML page with upload form + file listing.
+
+| Aspect | Detail |
+|---|---|
+| **How it works** | Go binary listens on a local port. Caddy reverse-proxies to it. Handles `POST /upload` (multipart form), serves `GET /` with a gallery page listing files, and `GET /photos/*` to serve files. |
+| **Dependencies** | Go binary (~5-10 MB). No runtime deps, no database. |
+| **Upload UX** | Full control — can build a drag-and-drop multi-file UI, show progress bars, rename duplicates. |
+| **Download UX** | Can serve individual files and generate a zip of all photos on-the-fly for bulk download. Thumbnails possible via Go's image stdlib. |
+
+**Pros:**
+- Total control over UX — can be exactly what you want.
+- Single static binary, trivially deployable. Copy & run.
+- Zip download, thumbnail generation, progress feedback — all easy in Go.
+- No plugin ecosystem dependency; you own the code.
+
+**Cons:**
+- You have to write and maintain it (even if small, ~200-400 lines).
+- Another process to manage alongside Caddy.
+- Feature creep risk — temptation to add more and more.
+
+---
+
+### C. WebDAV via Caddy + Caddy browse
+
+Caddy has a `webdav` plugin. WebDAV is HTTP-native file access — upload, download, directory listing — and modern browsers/OS file managers support it.
+
+| Aspect | Detail |
+|---|---|
+| **How it works** | Caddy serves a WebDAV endpoint for the photos directory. A static HTML page provides a browser-based upload form. File listing served via `file_server browse` on a separate route. |
+| **Dependencies** | Caddy + `webdav` plugin (`xcaddy build`). |
+| **Upload UX** | Friends can use the HTML form in browser, or mount the WebDAV share on their OS and drag files in. |
+| **Download UX** | Browse + click to download. OS-level mount allows bulk copy. |
+
+**Pros:**
+- Standard protocol — works with native OS file managers (macOS Finder, Windows Explorer, Linux Nautilus).
+- Upload and download in one protocol; no custom code.
+- Caddy ecosystem, single process.
+
+**Cons:**
+- WebDAV in browsers is inconsistent — you still need a static HTML upload form for reliable mobile UX.
+- Plugin needed (`xcaddy build`).
+- The "mount a network drive" step is too technical for most friends.
+- No gallery view, no thumbnails.
+
+---
+
+### D. FTP server with web frontend
+
+Run a lightweight FTP server (e.g., `vsftpd`, or a Go-based one like `ftpgrab/ftpserver`) and pair it with a web file manager frontend.
+
+| Aspect | Detail |
+|---|---|
+| **How it works** | FTP server exposes the photos directory. A web-based FTP client (e.g., Monsta FTP, or a minimal custom page) served by Caddy provides the browser UI. |
+| **Dependencies** | FTP daemon + web frontend + Caddy. |
+| **Upload UX** | Through the web frontend's upload widget. |
+| **Download UX** | Through the web frontend's file listing. |
+
+**Pros:**
+- FTP is battle-tested for file transfer.
+- Some web FTP clients provide decent UIs.
+
+**Cons:**
+- **Heaviest approach** — three components (FTP server, web UI, Caddy).
+- FTP is a legacy protocol with known security quirks (passive ports, plaintext by default). FTPS adds complexity.
+- Web FTP clients are often clunky, unmaintained, or PHP-based.
+- Overkill for the problem. Friends don't need FTP; they need a web page.
+- Firewall/NAT unfriendly compared to pure HTTP.
+
+---
+
+### E. Existing lightweight gallery software
+
+Deploy an existing open-source photo gallery (e.g., **PiGallery2**, **Photoview**, **Lychee**, **Photoprism**).
+
+| Aspect | Detail |
+|---|---|
+| **How it works** | Docker container or binary, reverse-proxied by Caddy. Provides web UI for upload, gallery view with thumbnails, download. |
+| **Dependencies** | Varies — some need a database (SQLite at minimum), Node.js, or Docker. |
+| **Upload UX** | Usually polished with drag-and-drop. |
+| **Download UX** | Gallery browsing with thumbnails, album zip downloads. |
+
+**Pros:**
+- Best UX out of the box — thumbnail grids, slideshows, EXIF data.
+- Actively maintained projects with communities.
+- Handles edge cases (duplicate detection, format support).
+
+**Cons:**
+- **Not lightweight** — most pull in a database, runtime, or Docker image.
+- Opinionated features you don't need (user management, albums, face detection).
+- Heavier operational footprint — updates, config, database migrations.
+- Misaligned: these are permanent gallery tools, not ephemeral upload-and-collect tools.
+
+---
+
+### Comparison Summary
+
+| Criterion | A. Pure Caddy | B. Go App | C. WebDAV | D. FTP + Web | E. Existing Gallery |
+|---|:---:|:---:|:---:|:---:|:---:|
+| Lightweight | ★★★★★ | ★★★★ | ★★★★ | ★★ | ★★ |
+| Browser UX | ★★ | ★★★★★ | ★★★ | ★★ | ★★★★★ |
+| Upload ease (mobile) | ★★★ | ★★★★★ | ★★★ | ★★ | ★★★★ |
+| Bulk download | ★★ | ★★★★★ | ★★★ | ★★★ | ★★★★ |
+| Ops simplicity | ★★★★★ | ★★★★ | ★★★★ | ★★ | ★★ |
+| Caddy integration | ★★★★★ | ★★★★ | ★★★★ | ★★★ | ★★★ |
+| Nothing to write | ★★★★ | ★★ | ★★★★ | ★★★★ | ★★★★★ |
+| Time to working MVP | ★★★★ | ★★★ | ★★★ | ★★ | ★★★★ |
+
+---
+
+## 5. Open Questions (Resolved)
+
+1. **Access control model** — Secret/unguessable URL path or basic auth. No accounts.
+2. **Photo volume** — ~10 people, ~200 photos total. Worst case ~4 GB (200 × 20 MB). Zip-on-the-fly is feasible at this scale.
+3. **Retention** — Ephemeral. Spin up for the collection window, then archive photos to personal storage and tear down.
+4. **Duplicate handling** — None. Keep everything as-is.
+5. **Upload size limits** — 20 MB per file as a safety cap.
+6. **Multi-trip reuse** — One directory per trip. Either clean the folder between trips or configure a new folder path. The Go binary takes the photo directory as a flag (e.g., `--dir ./crete-2026`).
+
+---
+
+## 6. Parties Involved
+
+| Role | Who |
+|---|---|
+| **Author** | You (server owner/operator) |
+| **Users** | Friends (non-technical, browser-only) |
+| **Operator** | You — deployment, maintenance, teardown |
+
+---
+
+## 7. Cross-Concerns
+
+### Security
+- **TLS mandatory** — Caddy handles this automatically with HTTPS.
+- **Access control** — at minimum, an unguessable URL path (e.g., `/holiday-crete-2026-a8f3b2c1/`). Optionally, Caddy `basicauth` for a shared password.
+- **Upload validation** — accept only image MIME types; reject executables. Enforce a max file size.
+- **Path traversal** — the application or Caddy handler must sanitize filenames to prevent `../../etc/passwd` style writes.
+
+### Privacy
+- Photos are personal. No analytics, no third-party scripts, no CDN.
+- Server-side: photos stored as flat files with no metadata indexing unless explicitly built.
+- After collection, the upload endpoint should be disabled (remove the route or stop the service).
+
+### Observability
+- Caddy access logs are sufficient. No need for metrics, APM, or dashboards for this scale.
+- Optional: log upload events (filename, size, timestamp) to a simple text file for your own tracking.