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>
7.9 KiB
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 Phase‑1
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 Phase‑2 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 atvoid.hynesy.combehind CF Access.- Sidebar (
public/components/sidebar.js) has sections: Spaces, Agents, Navigate. - Embedding precedent: the
Terminalitem is an<iframe src="/terminal/">thatserver.jsreverse-proxies (with WebSocket upgrade) to ttyd on CT 300.
- Sidebar (
- Timelapse —
/project/farm-timelapse, FastAPI + HTMX (Python) on CT 108, served attimelapse.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 cardpublic/views/cards/ai_usage.jswhoseFull dashboard ↗link currently points at the rawhttp://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 routestimelapse(#/timelapse) andai-usage(#/ai-usage).public/components/sidebar.js— add an "Apps" section withTimelapseandAI Usagenav items (active-highlight synced like existing items).public/views/timelapse.jsandpublic/views/aiusage.js— each mirrorspublic/views/terminal.js:- Void header bar:
◆ <Title>, an↗ Openlink to the standalone URL (target=_blank), and a⟳ Reloadbutton (f.src = f.src). - Full-height
<iframe>with correctsrc. Timelapse iframe getsallow="fullscreen"for video playback. - No back-to-Void affordance inside the embedded app.
- Void header bar:
public/app.js— wire the two new view modules into the render switch (matching howterminalis dispatched).public/views/cards/ai_usage.js(line ~33) — retargetFull dashboard ↗fromhttp://192.168.1.212:8080/to in-Void#/ai-usage(droptarget=_blankso it opens session-shared within the SPA). Card otherwise unchanged.package.json/ version string inserver.js— bump to2.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.comDNS 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 existingtimelapseAccess 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/--badas in Void. - Fonts: display
Cinzel/Cormorant Garamondserif, bodyCormorant Garamond, UIsystem-ui, monoJetBrains Mono.
- Palette:
- 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
#/timelapseand#/ai-usage; sidebar renders the two Apps items; each view mounts an iframe with the expectedsrc; the SV card link points at#/ai-usage. - CF / infra:
curl -I https://aiusage.hynesy.com→ CF Access challenge when unauthenticated;200with a valid session/service token. - Playwright (webapp-testing): click each rail item → embed loads; visit
timelapse.hynesy.comandaiusage.hynesy.comdirectly → no Void rail present.
Sequencing & safety
- Provision
aiusage.hynesy.com(Traefik route + CF Access app); validate iframe SSO renders without a login wall before touching Void. - Void changes: Apps section + routes + two views + SV-card retarget + version
bump →
alpha.27. Deploy via existingdeploy/. - Timelapse Phase‑1 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 Phase‑2 full component restyle (preview first).
- Void 1 / CT 301 teardown (separate infra effort).
- Migrating the Terminal rail item into the Apps section.