Files
Void-Homelab/docs/superpowers/specs/2026-06-08-fold-in-timelapse-aiusage-design.md
root 4f97add050 docs(fold-in): spec for folding Timelapse + AI Usage into the Void
Cross-origin HTTPS iframe embeds as left-rail "Apps" items; standalone
URLs stay chromeless. phuryn gets aiusage.hynesy.com behind CF Access;
timelapse gets a Phase-1 palette/typography restyle. Targets alpha.27.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:32:05 +10:00

153 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design: Fold Timelapse + AI Usage into the Void
**Date:** 2026-06-08
**Status:** Approved (brainstorm), pending implementation plan
**Target version:** void-server `2.0.0-alpha.27`
## Summary
Make the **Timelapse** app and the **AI Usage (phuryn/claude-usage)** dashboard
first-class, navigable items in the Void's left rail, embedded as **cross-origin
HTTPS iframes**. Each app remains reachable at its own `*.hynesy.com` URL, and
when accessed there it shows only the bare app — no Void chrome, so there is no
way to "backtrack" into the Void. The Timelapse app additionally gets a Phase1
restyle (palette + typography) to align with the Void aesthetic.
This is the **fold-in feature**. Three things are explicitly **out of scope**
here and tracked separately:
- The phuryn dashboard "not really working" **functional fix** (deferred by user).
- The Timelapse **Phase2 full visual match** (preview-first follow-up).
- **Void 1 / CT 301 teardown** (separate infra effort, sequenced after this).
- Moving the existing **Terminal** rail item into the new section.
## Background / current state
- **Void 2** — `/project/src/void-v2`, Express + hash-routed SPA, deployed to
CT 311 (`192.168.1.216`), served at `void.hynesy.com` behind CF Access.
- Sidebar (`public/components/sidebar.js`) has sections: Spaces, Agents, Navigate.
- **Embedding precedent:** the `Terminal` item is an `<iframe src="/terminal/">`
that `server.js` reverse-proxies (with WebSocket upgrade) to ttyd on CT 300.
- **Timelapse** — `/project/farm-timelapse`, FastAPI + HTMX (Python) on CT 108,
served at `timelapse.hynesy.com` (CF tunnel → Traefik on CT 100 → CT 108:8000,
behind CF Access / Google IdP).
- **AI Usage** — `phuryn/claude-usage`, a Python-stdlib web SPA on **CT 300**
(`192.168.1.212:8080`). Its data source is CT 300's local
`/root/.claude/projects/**/*.jsonl` (Claude Code transcripts). There is also a
Sacred Valley card `public/views/cards/ai_usage.js` whose `Full dashboard ↗`
link currently points at the raw `http://192.168.1.212:8080/`.
### Why phuryn stays on CT 300
Its entire data source is CT 300's local Claude Code transcripts. Relocating it
to the Void CT would require continuously syncing those JSONL files across hosts
for no benefit. Void already proxies to CT 300 for the Terminal, so CT 300 is an
already-trusted backend.
## Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Embed strategy | **A — cross-origin HTTPS iframes** | Both apps use root-relative paths; a same-origin prefix proxy would collide with Void's own routes (timelapse's hardcoded `/browse…`, phuryn's `fetch('/api/…')`). Cross-origin iframes to each app's own HTTPS origin sidestep all path rewriting. |
| phuryn hostname | **`aiusage.hynesy.com`** | New CF tunnel host → CT 300:8080 behind CF Access. Also *adds* auth to phuryn, which is currently unauthenticated on the LAN. |
| Timelapse standalone URL | `timelapse.hynesy.com` (unchanged) | Already HTTPS, no `X-Frame-Options`, directly frameable. |
| SV AI Usage card | **Keep as-is**, retarget its link only | Card stays; `Full dashboard ↗` retargets to in-Void `#/ai-usage`. |
| Rail placement | New **"Apps"** sidebar section | Groups embedded external apps; Terminal stays under Navigate (moving it is out of scope). |
| Timelapse restyle | **Phase 1 = palette + typography only** | Fast, low-risk, no markup restructure. Phase 2 deferred. |
## Architecture
Void renders, per app, a thin Void-styled header bar + a full-height `<iframe>`
pointing at the app's own HTTPS origin. No "back to Void" affordance is injected
into either app, so standalone visits stay chromeless.
| App | Standalone URL | Backend host | Iframe `src` |
|---|---|---|---|
| Timelapse | `timelapse.hynesy.com` (exists) | CT 108 | `https://timelapse.hynesy.com` |
| AI Usage | `aiusage.hynesy.com` (new) | CT 300:8080 | `https://aiusage.hynesy.com` |
## Components
### Void (CT 311, `/project/src/void-v2`)
- `public/router.js` — add routes `timelapse` (`#/timelapse`) and `ai-usage`
(`#/ai-usage`).
- `public/components/sidebar.js` — add an **"Apps"** section with `Timelapse`
and `AI Usage` nav items (active-highlight synced like existing items).
- `public/views/timelapse.js` and `public/views/aiusage.js` — each mirrors
`public/views/terminal.js`:
- Void header bar: `◆ <Title>`, an `↗ Open` link to the standalone URL
(`target=_blank`), and a `⟳ Reload` button (`f.src = f.src`).
- Full-height `<iframe>` with correct `src`. Timelapse iframe gets
`allow="fullscreen"` for video playback.
- No back-to-Void affordance inside the embedded app.
- `public/app.js` — wire the two new view modules into the render switch
(matching how `terminal` is dispatched).
- `public/views/cards/ai_usage.js` (line ~33) — retarget `Full dashboard ↗`
from `http://192.168.1.212:8080/` to in-Void `#/ai-usage` (drop `target=_blank`
so it opens session-shared within the SPA). Card otherwise unchanged.
- `package.json` / version string in `server.js` — bump to `2.0.0-alpha.27`;
CHANGELOG entry.
### Cloudflare / infra (provisioned via CF API; creds in memory)
- **Traefik (CT 100) dynamic config:** router for `aiusage.hynesy.com`
`http://192.168.1.212:8080`. Wildcard `*.hynesy.com` DNS already targets the
tunnel, so no new DNS record is required.
- **CF Access application** for `aiusage.hynesy.com` — Google IdP + email
allowlist, cloned from the existing `timelapse` Access app.
### Timelapse restyle — Phase 1 (CT 108, `/project/farm-timelapse`)
- Port Void's design tokens into `tlcapture/static/style.css`:
- Palette: `--bg #0a0a0e`, `--panel #14141c`, `--panel-2 #1c1c26`,
`--border #2a2a36`, `--text #e8e6ed`, `--muted #888094`,
`--accent #ff4f2e` (blackflame), `--ok/--warn/--bad` as in Void.
- Fonts: display `Cinzel`/`Cormorant Garamond` serif, body
`Cormorant Garamond`, UI `system-ui`, mono `JetBrains Mono`.
- Colors, typography, and surface backgrounds only. **No markup restructure**
(that is Phase 2). The app keeps its own internal navigation/controls.
## Data flow
No new data paths. phuryn keeps reading CT 300's local JSONL transcripts; only
its *exposure* changes (LAN `:8080` → additionally `aiusage.hynesy.com` via
Traefik/CF Access). Timelapse is functionally untouched; only its CSS changes.
## Error handling
Cross-origin iframes cannot report load status to the parent, so each Void view
always surfaces an `↗ Open in new tab` fallback in its header (usable even if the
frame is blank) plus a `⟳ Reload`. If CF Access ever shows a login wall inside
the frame, the fallback link routes the user there at top level. Validating that
CF Access SSO renders frame-inline (no login wall, given an existing session) is
the **first** implementation step.
## Testing
- **vitest + jsdom:** router resolves `#/timelapse` and `#/ai-usage`; sidebar
renders the two Apps items; each view mounts an iframe with the expected `src`;
the SV card link points at `#/ai-usage`.
- **CF / infra:** `curl -I https://aiusage.hynesy.com` → CF Access challenge when
unauthenticated; `200` with a valid session/service token.
- **Playwright (webapp-testing):** click each rail item → embed loads; visit
`timelapse.hynesy.com` and `aiusage.hynesy.com` directly → no Void rail present.
## Sequencing & safety
1. Provision `aiusage.hynesy.com` (Traefik route + CF Access app); **validate
iframe SSO** renders without a login wall before touching Void.
2. Void changes: Apps section + routes + two views + SV-card retarget + version
bump → `alpha.27`. Deploy via existing `deploy/`.
3. Timelapse Phase1 restyle. Deploy via `scripts/install.sh`.
`pct snapshot` CT 311 before the Void deploy and CT 108 before the restyle
deploy (backup-before-changes rule).
## Out of scope (tracked elsewhere)
- phuryn functional fix ("not really working").
- Timelapse Phase2 full component restyle (preview first).
- Void 1 / CT 301 teardown (separate infra effort).
- Migrating the Terminal rail item into the Apps section.