lbzfai.com ↔ Jetson dashboard integration
Bucket: Backend (Agent B) Status: Reviewed — 2026-05-12 (Phase B seams applied: Cloudflare Tunnel parked per ADR-005) Owner: Sophia · Reviewers: Andrew, Agent C (front-end consumer), Agent A (network / Tailscale)
Context
Section titled “Context”lbzfai.com is a static Astro page with Auth0 login. The CV monitoring runs on a Jetson on LBZF’s local network with no public IP. The question this doc answers: how do authenticated users on lbzfai.com see Jetson data in Phase I?
ADR-005 (Tailscale-only Phase I) locked the answer: Phase I users install Tailscale and reach the Jetson dashboard directly (http://<jetson-tailscale-ip>:5000). lbzfai.com stays a static + Auth0 landing; it does not proxy live Jetson data.
The outbound-tunnel + Worker-as-proxy architecture proposed in round-1 is parked to docs/design/60-parking/cloudflare-tunnel-from-jetson.md and resumes in Phase II if Mariana (or a future executive user) refuses Tailscale.
- Phase I path for authenticated users to reach Jetson data without exposing the Jetson to the public internet.
- Identity flows cleanly from Auth0 login → role-aware UI on lbzfai.com → user clicks “Open dashboard” → Tailscale-direct to the Jetson where the same identity is recognized.
- Works for Sophia, Andrew, Armando, Ronald, Mariana, and the ITBA team (all install Tailscale; Tailscale paid tier per ADR-005).
- A documented migration path to Phase II Cloudflare-Tunnel-mediated proxy access (preserved in parking).
Non-goals
Section titled “Non-goals”- Mariana viewing live data on her phone without Tailscale — Phase II per ADR-005.
- Public video streaming. Phase I is JSON dashboards + small frame thumbnails reached via Tailscale only.
- Multi-Jetson load balancing — Phase I has one LBZF Jetson + one ITBA twin, both on Tailscale with different tags (
tag:lbzf-pereira,tag:itba-dev). - Replacing Tailscale; Tailscale is the secure transport.
Phase I architecture — Tailscale-direct, lbzfai.com stays static
Section titled “Phase I architecture — Tailscale-direct, lbzfai.com stays static” public internet user browser ───────── https://lbzfai.com ───────── Cloudflare Workers (any device, │ │ on Tailscale) │ ▼ │ static Astro shell │ + Auth0 SPA login │ + role-aware /app/ pages │ (NO Jetson proxy) ▼ Auth0 ◀──── login redirect │ ▼ Auth0 JWT contains: https://lbzfai.com/role (singular string) https://lbzfai.com/org_id (integer) https://lbzfai.com/module_scope (array or null) │ ▼ /app/ingeniero (Ronald) or /app/ejecutivo (Mariana) │ shows: "Open dashboard" link │ ▼ user clicks → http://<jetson-tailscale-ip>:5000 │ ▼ Tailscale wire ┌────────────────┐ │ Jetson │ │ :5000 ───▶ │ FastAPI dashboard │ │ ─ Tailscale-User-Login header → identity │ │ ─ users.json lookup → role + module_scope │ │ ─ endpoint role checks └────────────────┘Identity on the Jetson
Section titled “Identity on the Jetson”Jetson FastAPI middleware resolves identity in this order:
Tailscale-User-Loginheader (set by Tailscale Serve when reached through Tailscale’s HTTPS proxy) — Phase I primary path.- Local cookie session (
/auth/localbreak-glass — seedashboard-api.md). - Reject — 401.
Auth0 JWT validation on the Jetson is not wired in Phase I. The Worker-validated-JWT → X-LBZF-Identity header flow is preserved in 60-parking/cloudflare-tunnel-from-jetson.md for Phase II.
lbzfai.com role-aware UI
Section titled “lbzfai.com role-aware UI”The static Astro app reads role from the Auth0 access token’s https://lbzfai.com/role custom claim (see user-org-and-auth.md) and renders one of:
/app/admin(Sophia, Andrew) — admin home./app/ingeniero(Ronald) — landing page with “Abrir tablero (Tailscale)” link to the Jetson./app/ejecutivo(Mariana) — placeholder dashboard with mocked aggregates in Phase I; Phase II wires the Worker proxy from the parked design and renders real numbers./app/investigacion(parked, see60-parking/full-role-matrix.md)./app/cuenta— fallback for users with no assigned role.
No /api/* fetch from the browser to a Worker in Phase I. The Worker’s role today is purely to gate the static pages by role claim.
Failure modes (Phase I)
Section titled “Failure modes (Phase I)”| Failure | Behavior |
|---|---|
| Tailscale partitioned (user can’t reach Jetson) | lbzfai.com still loads; “Open dashboard” link fails until Tailscale reconnects. UI shows a “Sin conexión a la planta” banner per localization-es-co.md. |
| Auth0 down | Login fails on lbzfai.com; users already on Tailscale can still hit the Jetson directly with the local-auth break-glass route. |
| Jetson down | Both surfaces show “factory offline.” |
| Cloudflare Workers down | lbzfai.com itself goes down (acceptable; uptime is very high). |
| User on cellular without Tailscale | Cannot reach the Jetson. lbzfai.com loads but Ronald/Mariana sees a “Install Tailscale to view live data” instruction page. Phase II removes this constraint via the parked Worker proxy. |
Cost (Phase I)
Section titled “Cost (Phase I)”- Cloudflare Workers Static Assets: free tier covers Phase I traffic.
- Tailscale paid Starter tier needed by 2026-05-15: ~$6/user/mo × ~9 users ≈ $45/mo. Free tier blocks at 3 users.
- Auth0: free dev tenant covers <7,000 MAU.
- No outbound proxy infrastructure: $0.
Total: ~$45/mo for the integration layer in Phase I (the Tailscale paid tier per ADR-005).
Migration path to Phase II (Worker proxy via Cloudflare Tunnel)
Section titled “Migration path to Phase II (Worker proxy via Cloudflare Tunnel)”Preserved verbatim in docs/design/60-parking/cloudflare-tunnel-from-jetson.md. Resume that doc when Mariana refuses Tailscale or a non-Tailscale user joins the project.
Alternatives considered (Phase I)
Section titled “Alternatives considered (Phase I)”- Cloudflare Tunnel + Worker proxy — the round-1 default; parked per ADR-005. See above.
- Fly.io / EC2 Tailscale-egress box proxying for the Worker — equivalent shape to the parked outbound-tunnel design, slightly more ops; same Phase I non-need.
- Direct Worker→Jetson with a public Tailscale Funnel URL — exposes the Jetson; rejected.
- NGINX on the Jetson as the public ingress — requires opening a port through LBZF’s NAT; rejected.
- Skip lbzfai.com entirely; ship a desktop app — fails Andrew’s “full prod stack day 1” framing.
Open questions
Section titled “Open questions”- OPEN: Tailscale Serve / Funnel plan tier confirmation — Sophia must confirm the paid Starter tier supports identity-header injection. Owner: Sophia, target: 2026-05-13 (before BA handoff).
- OPEN: How does Mariana access the dashboard the first time? lbzfai.com → Auth0 invite email → role granted via PR → install Tailscale → “Open dashboard”. Need an onboarding doc. Owner: Sophia + Armando.
- OPEN: Can ITBA’s twin Jetson share
lbzfai.com? Yes — same Cloudflare Worker, but the “Open dashboard” link reads from the user’shttps://lbzfai.com/org_idclaim and routes to the right Tailscale node. Confirm wiring before 2026-05-15. Owner: Andrew + ITBA. - OPEN: What’s the SLO? “Available during LBZF working hours (6am–6pm CO, Mon–Fri).” Owner: Sophia / Andrew.
Cross-bucket dependencies
Section titled “Cross-bucket dependencies”| This doc depends on | Owner bucket | What we need |
|---|---|---|
| Auth0 custom-claim Action | This bucket (user-org-and-auth.md) | JWT contains role / org_id / module_scope under https://lbzfai.com/… namespace |
| Dashboard API on Jetson | This bucket (dashboard-api.md) | Endpoints exist; middleware reads Tailscale-User-Login header |
| Tailscale set up on Jetson | Hardware (Agent A) + ADR-005/006 | Already locked |
| This doc implies | Owner | Ask |
|---|---|---|
Frontend role-aware Astro pages without any /api/* proxy in Phase I | Agent C | Plumb the JWT role claim only |
| Tailscale install runbook for Mariana / Ronald / ITBA | Sophia + Armando | Phase I prerequisite |
What’s weak in this doc
Section titled “What’s weak in this doc”- The “Install Tailscale” gate excludes anyone unwilling to install client software. Mariana might object; if she does, ADR-005 gets re-opened and the parked Worker-proxy design comes back.
- No fallback when Cloudflare itself is degraded. lbzfai.com goes down; users on Tailscale can still hit the Jetson directly though, which is a nice property of this architecture.
- The lbzfai.com role-aware UI shows aggregates that aren’t real in Phase I (
/app/ejecutivois mocked). Mariana opens the page and sees fake numbers labeled clearly as such. Round-2 critique flagged this; the Phase II Worker proxy is what fills in real data. - Identity on the Jetson relies entirely on Tailscale’s identity-header injection. If Tailscale’s Serve mode misconfigures or downgrades that feature, the break-glass
/auth/localis the only fallback.
Rollout
Section titled “Rollout”- Now (May 2026): Auth0 custom claims live; Astro pages render role-aware shells; Tailscale paid tier upgrade by 2026-05-15.
- Before Argentina (2026-05-15): end-to-end Tailscale demo: Sophia logs in at lbzfai.com → sees admin role → clicks “Open dashboard” → reaches Jetson over Tailscale.
- Before Pereira (Jul 2026): Ronald, Mariana, ITBA all enrolled in the tailnet; tested end-to-end.
- Day-1 Pereira: documented runbook for “Tailscale is offline; here’s how to check.”
- Phase II: thaw
60-parking/cloudflare-tunnel-from-jetson.mdif Mariana-no-Tailscale becomes a hard requirement.
Unblocks: every “lbzfai.com authenticates users” deliverable; Argentina demo end-to-end.