**Bead** `06b7373f-2ba9-46c5-88bb-31bd93ce453f` · [canonical URI](https://redfish.acequia.io/guerin/.agents/06b7373f-2ba9-46c5-88bb-31bd93ce453f/) · discussion: Talk: Android Photo Sync This page is the wiki facet of a [bead](https://redfish.acequia.io/guerin/.agents/); the bead is the source of truth.
## Identity - **Bead URI:** https://redfish.acequia.io/guerin/.agents/06b7373f-2ba9-46c5-88bb-31bd93ce453f/ - **GUID:** `06b7373f-2ba9-46c5-88bb-31bd93ce453f` - **Opened:** 2026-06-27
## Motivation (why a GUID) Sync photos from an Android phone (Samsung Galaxy S23) into `stephenguerin.live/photos/s23/` from a browser page: pick a folder with the File System Access API, virtual-scroll a large library (~12000 images) without melting the DOM, select photos, and PUT them to a new write endpoint on the `stephenguerin.live` origin server. A GUID keeps this device-sync work out of the global namespace.
## Mechanism (what this bead does) Two deliverables, both verified: 1. **Browser gallery** — [artifacts/photo-sync-gallery.html](2026-06-27/artifacts/photo-sync-gallery.html) (deployed to [`stephenguerin.live/photos/index.html`](c:/Users/steph/Documents/sites/stephenguerin.live/photos/index.html), served at `https://stephenguerin.live/photos/`). Recycled-DOM virtual scroller: a fixed pool of tiles repositioned via `transform`; only the visible window + buffer ever call `getFile()` / `createObjectURL` (object URLs revoked on recycle), so the 12000-image directory is enumerated as metadata only, never fully decoded. Tap-to-select, per-file `PUT` with a bearer token. 2. **Server `/photos` lane** — first added inline to [`server.js`](C:/caddy/origins/stephenguerin.live/server.js), then **refactored into a drop-in handler** once the user asked for hot-pluggable per-subpath handlers (index.php/ASP style): - A generic **filesystem router** in `server.js` (deepest `handler.mjs`/`index.mjs` under `WEB_ROOT` wins, hot-loaded by mtime) — [notes/01-filesystem-router.md](2026-06-27/notes/01-filesystem-router.md). - The photos policy now lives in [`stephenguerin.live/photos/handler.mjs`](c:/Users/steph/Documents/sites/stephenguerin.live/photos/handler.mjs) + [`config.json`](c:/Users/steph/Documents/sites/stephenguerin.live/photos/config.json), not the monolith. Contract in [notes/00-photos-endpoint.md](2026-06-27/notes/00-photos-endpoint.md).
## Folder Layout ``` 06b7373f-.../ ├── about.md ├── uploads/ # inbound dock └── 2026-06-27/ ├── notes/00-photos-endpoint.md ├── artifacts/photo-sync-gallery.html └── chats/2026-06-27-android-photo-sync.md ```
## Session Log - **2026-06-27** — Built the gallery + `/photos` PUT lane. New code validated on a throwaway instance (`:3599`): 401 on bad/no token, 201 + binary round-trip on valid token, 403 on path escape and on overwriting `index.html`. **Live service still on old code** — needs an elevated `Restart-Service stephenguerin-live-origin` to go live.
## Modus Operandi (how to interact) On the phone: open `https://stephenguerin.live/photos/`, paste the upload token once (stored in `localStorage`), Pick Folder, tap photos, Upload. Destination subfolder defaults to `s23`.
## Capabilities - File System Access API folder pick (`<input type=file>` fallback when absent). - Virtual-scroll pooled DOM; bounded memory regardless of library size. - Token-authorized, binary-safe `PUT /photos/<dest>/<name>`; `GET` serves them back.
## Lifecycle Active. Apoptosis: when sync is routine, fold the endpoint doc into the stephenguerin.live router bead docs and close. Token is revocable by rotating `.credentials/stephenguerin-photos-token.txt`.
## Limitations - Live cutover needs one elevated service restart (not doable from this unprivileged session). - Single shared bearer token (no per-device attenuation yet). - `showDirectoryPicker` availability on Android Chrome is the user's claim; `<input type=file>` fallback covers the gap.
See all: Beads