Skip to content

Reference hyperframes hub

What it is

A Flask app at https://hyperframes.justinsforge.com/ that: - Shows a card grid of Hyperframes projects (auto-discovered by scanning forge/data/video-edit-eval/round1-hyperframes/*/hyperframes.json). - Gates everything behind a PIN (currently 6225, hardcoded constant in app.py). - Each card links to /projects/<name> — a CapCut-style viewer with: header (hamburger / project name / reload), iframe of that project's Studio, chat panel that sends prompts to claude -p and polls status. - Hub home button rule: every page links to https://justinsforge.com/ (in the drawer for project pages, fixed pill on the hub page).

Where the code lives

File Role
forge/data/video-edit-eval/_hub/app.py Flask app (~200 lines). Routes: /, /login, /logout, /projects/, /api/reprompt/, /api/status/, /api/health.
forge/data/video-edit-eval/_hub/templates/base.html Layout shell. Versioned CSS link via static_v('hub.css') context processor.
forge/data/video-edit-eval/_hub/templates/login.html PIN entry form.
forge/data/video-edit-eval/_hub/templates/hub.html Card grid + sign-out link.
forge/data/video-edit-eval/_hub/templates/project.html Header + drawer + iframe + chat. JS for drawer open/close, reprompt POST, status polling.
forge/data/video-edit-eval/_hub/templates/not_found.html 404.
forge/data/video-edit-eval/_hub/static/hub.css All styles. Mobile breakpoint at 1024px (project page) and 800px (hub page).
forge/data/video-edit-eval/_hub/state/sessions/<token> File-backed PIN session tokens (touch the file = active session).
forge/data/video-edit-eval/_hub/state/jobs/<id>.json Per-reprompt job state (status, stdout/stderr tails).
forge/scripts/forge_hyperframes_hub.py Project registry + filesystem scanner. PROJECT_REGISTRY dict maps folder name → port + subdomain. Imported by app.py and the new-project script.
forge/scripts/forge_hyperframes_preview_runner.py systemd ExecStart wrapper for each project's preview server.
forge/scripts/forge_hyperframes_new_project.py End-to-end project bootstrap (init + port pick + registry update + Cloudflare ingress + systemd enable + hub regen).
forge/.claude/skills/hyperframenewproject/SKILL.md User-facing skill that wraps the bootstrap script.

systemd units (durable across reboots)

Unit What it runs
forge-hyperframes-hub.service python3 _hub/app.py on 127.0.0.1:4900. Cloudflare ingress at hyperframes.justinsforge.com.
forge-hyperframes-preview@<name>.service Template unit. One instance per project, runs forge_hyperframes_preview_runner.py <name> which looks up port in PROJECT_REGISTRY and execs npx hyperframes preview --port N.

sudo systemctl list-units 'forge-hyperframes-*' to list all instances.

Cache strategy

  • Static files served with SEND_FILE_MAX_AGE_DEFAULT = 0.
  • CSS/JS links in templates are versioned via ?v={{ static_v('hub.css') }} (mtime-stamp). Every edit auto-invalidates the browser cache.
  • Cloudflare edge may still cache for a few hours, but the version stamp sidesteps that since the URL changes on every edit.

PIN auth (file-backed)

  • Login posts to /login with field pin. Constant-time compared against PIN = "6225" in app.py.
  • Success: writes a random URL-safe token file at state/sessions/<token>, sets forge_hf_pin cookie (HttpOnly, Secure, SameSite=Lax, 30-day expiry).
  • Every request before the gate checks cookie token → file exists. Logout deletes the file + cookie.
  • This is not production-grade auth. Fine for a private hub. For broader sharing, swap to Cloudflare Access.

Reprompt loop (real, not stubbed)

  • Project page POSTs /api/reprompt/<name> with {prompt: "..."}.
  • Backend writes a job file with status: "queued", then spawns a thread that runs:
    claude -p "<wrapped prompt>" --dangerously-skip-permissions
    
    in the project directory. The wrapper prompt constrains: edit only index.html, run npm run check after.
  • Status polls every 1.5s. Statuses: queuedrunningdone (rc=0), failed (rc≠0, captures stderr tail), timeout (10 min cap), error.
  • On done, the iframe is reloaded from the client side so the new render shows.
  • Cost: every reprompt burns Claude subscription quota. ~one small interactive turn per call. No external API spend.

How to add a new project

Use the skill: /hyperframenewproject <kebab-case-name> [--aspect 16:9|9:16].

The skill (script: forge_hyperframes_new_project.py) does: 1. npx hyperframes init <name> --tailwind in round1-hyperframes/. 2. Patches index.html to the requested aspect. 3. Picks the next free port starting at 3003. 4. Appends to PROJECT_REGISTRY in forge_hyperframes_hub.py (regex insert, idempotent). 5. cf-add <name>-hf justinsforge.com http://localhost:<port> --tunnel "VR Alliance". 6. sudo systemctl enable --now forge-hyperframes-preview@<name>.service. 7. Regenerates the hub static fallback (legacy, not required by Flask app).

How to remove a project

sudo systemctl disable --now forge-hyperframes-preview@<name>
/home/justinwieb/forge/scripts/forge_cloudflare_cf.py rm <name>-hf justinsforge.com --tunnel "VR Alliance"
rm -rf /home/justinwieb/forge/data/video-edit-eval/round1-hyperframes/<name>
# Edit forge_hyperframes_hub.py and remove the PROJECT_REGISTRY entry
sudo systemctl restart forge-hyperframes-hub  # picks up the registry change

Doctrine that applies

  • feedback_home_button_on_subdomains.md — every authored x.justinsforge.com page must have a link home (drawer satisfies this on the project page).
  • feedback_new_subdomain_links_home.md — when adding a new subdomain anywhere, also add a tile to forge/sites/justinsforge.com/landing/index.html. Per-project Hyperframes children are exempt (they live inside the hub, not on home).

Common ops

Task Command
Restart hub after editing app.py sudo systemctl restart forge-hyperframes-hub
View hub logs sudo journalctl -u forge-hyperframes-hub -f
View a project's preview logs sudo journalctl -u forge-hyperframes-preview@<name> -f
Restart a project's preview sudo systemctl restart forge-hyperframes-preview@<name>
Audit ingress vs registry see feedback_new_subdomain_links_home.md for the diff one-liner
Local debug (Flask dev server) cd _hub; python3 app.py (waits on :4900)

[Claude Code]