Information architecture & sitemap
S3 Worker-proxy path deferred per ADR-005 (Tailscale-only Phase I). Worker-proxy design preserved in
docs/design/60-parking/cloudflare-tunnel-from-jetson.md.
Status: draft Author: Agent C (frontend bucket, round 1) Reviewers: Agent B (integration spec), Sophia Mann, Andrew Kent; round-2 critique agent Last updated: 2026-05-10
Context
Section titled “Context”Today lbzfai.com is a single Astro page with Auth0 SPA login that, post-login, does nothing. Andrew Kent’s 2026-05-10 directive (“all the nested pages users will see on the platform”) requires planning the full page graph before we start adding routes ad-hoc. The v1.9 plan stipulates a Flask/FastAPI dashboard on :5000 running on the Jetson, accessed via Tailscale — but it does not say where lbzfai.com fits in relation to that dashboard, or what lives where. This doc resolves that.
Anchor artifacts:
~/lbzfai/src/pages/index.astro(the current one and only page)~/lbzfai/CLAUDE.md§“Architecture” (Astro static + client-side Auth0 + Cloudflare Workers w/ Static Assets)docs/technical/architecture.md(Jetson dashboard on :5000, Tailscale for remote)docs/technical/deployment.md(Tailscale-or-bust ops model)docs/factory/current-state.md(the whiteboard + Excel loop the dashboard replaces)
- A complete page graph for the platform — every URL a user can land on, every nav edge between them.
- An explicit, defended answer to: does lbzfai.com host the dashboard, or does it redirect authenticated users to the Jetson? This is the most consequential frontend decision in the project.
- Route names that survive into Phase II/III (4 modules, ~20 cameras, future supervisors).
- A nav graph that doesn’t require a real router today — i.e., the IA is consistent with
output: 'static'Astro plus Auth0 SPA gating for as long as Phase I needs. - Spanish-first URL semantics where it doesn’t hurt (e.g.,
/modulos/angelareads better to Ronald than/modules/angela).
Non-goals
Section titled “Non-goals”- The visual design of each page. That lives in the per-persona UX docs.
- The backend data contract that powers each page. That’s Agent B’s territory.
- The marketing/landing copy for the public lbzfai.com home page.
- Mobile-app native shells. Out of scope through Phase III.
Proposed approach
Section titled “Proposed approach”The render-location question (most important)
Section titled “The render-location question (most important)”Question. Where does Ronald’s dashboard actually render — on lbzfai.com pulling Jetson data through Cloudflare, or directly on the Jetson via Tailscale on http://<jetson-tailscale-ip>:5000?
Three viable architectures:
| Option | What it is | Pros | Cons |
|---|---|---|---|
| A. Tailscale-direct | lbzfai.com is only marketing + login. Authenticated users see a “Open dashboard” link that points to http://<jetson-tailscale-ip>:5000. Tailscale must be installed on every user’s device. | Simplest. Matches v1.9 plan literally. No data leaves the Jetson. No new cloud infra needed. | Mariana cannot use it from her phone unless she installs Tailscale. Public URL bar shows raw IP. Cannot SSO across the two surfaces easily. Hard to share a screenshot or a link with anyone. |
| B. Cloudflare-proxied | lbzfai.com hosts every page. A Cloudflare Worker proxies a narrow JSON API to the Jetson over Tailscale (the Worker is a Tailscale node). Live video is either tunneled or replaced with periodic stills. | One URL for everyone. Mariana on her phone needs nothing installed. Auth0 covers everything. We control the public surface. | Real infra to maintain (Worker-as-Tailscale-node is non-trivial). Data leaves the Jetson. Latency budget tightens. New attack surface. |
| C. Hybrid | lbzfai.com hosts the executive dashboard (Mariana — slow-moving aggregate data, no live video). Ronald’s engineer dashboard stays on http://<jetson>:5000 via Tailscale. | Pragmatic: Mariana never needs Tailscale; Ronald’s working dashboard stays low-latency and local. | Two codebases, two auth surfaces (potentially), users hit different URLs depending on role. Documentation overhead. |
Proposed answer (Frontend bucket recommendation): Option C — Hybrid. Justification:
- Mariana on her phone is the hardest constraint. She will not install Tailscale on personal devices for a family-business product (assumption — see open question). Option A breaks her. Option B works but introduces a Tailscale-aware Cloudflare Worker — a significant new piece of infra that has to be production-grade by July 2026.
- Ronald is already at a desk inside the plant. His latency budget is sub-second. Direct Jetson access over local LAN (when in-plant) or Tailscale (when remote) is the right answer for him. Putting his dashboard behind a public URL adds latency and risk for zero user benefit.
- The hybrid contains the blast radius of either failure mode. If the Cloudflare Worker breaks, Mariana’s view degrades to “see Ronald’s last shared screenshot” — Ronald keeps working. If Tailscale breaks, Ronald loses his dashboard but Mariana’s executive view still works on cached data.
- It matches the v1.9 plan. The v1.9 plan already names a Flask dashboard on
:5000accessed via Tailscale. Option C keeps that intact and adds an executive surface on top.
Owner of the final call: Agent B (integration spec). This doc proposes the answer; Agent B’s spec must accept, reject, or revise. If Agent B rejects, every section below shifts — see [What’s weak in this doc] §1.
Page graph (assuming Option C)
Section titled “Page graph (assuming Option C)”lbzfai.com (PUBLIC + AUTHENTICATED — Cloudflare Workers static)│├── / [public] Marketing landing. Current page, lightly refactored.├── /entrar [public] Auth0 login. Or stays at "/" with login button — see below.├── /salir [auth] Auth0 logout target.│├── /app [auth] Application shell. Role-aware: redirect based on Auth0 role to /app/ejecutivo or /app/ingeniero.│ ││ ├── /app/ejecutivo [executive, admin] Mariana's home dashboard.│ │ ├── /app/ejecutivo/modulo/angela [executive, admin] Per-module executive view.│ │ ├── /app/ejecutivo/modulo/carmenza [executive, admin] Phase III.│ │ ├── /app/ejecutivo/modulo/camisetas [executive, admin] Phase III.│ │ ├── /app/ejecutivo/modulo/empaque [executive, admin] Phase III.│ │ └── /app/ejecutivo/historico [executive, admin] Trend / monthly summary.│ ││ ├── /app/ingeniero [engineer, admin] "Go to the on-plant dashboard" landing + status panel.│ │ ├── /app/ingeniero/abrir-jetson [engineer, admin] Smart redirect: tries http://<jetson-tailscale-ip>:5000;│ │ │ falls back to instructions if user is not on Tailscale.│ │ └── /app/ingeniero/exportar [engineer, admin] Excel export trigger UI (the export job itself runs on the Jetson).│ ││ ├── /app/investigacion [research, admin] ITBA view. Read-only aggregate; see auth0-roles-and-access.md.│ ││ ├── /app/admin [admin] Sophia/Andrew. Role management, audit, deploy status.│ ││ └── /app/cuenta [any auth] User account: language preference, sign out.│└── /soporte [public] Static support / contact page. EN+ES.http://<jetson-tailscale-ip>:5000 (AUTHENTICATED via Tailscale + a separate password or a Cloudflare Tunnel that brokers Auth0 — see below)│├── / Ronald's home dashboard (the most important screen — see ronald-dashboard-ux.md).├── /modulo/<name> Per-module live view (Angela in Phase I).├── /modulo/<name>/puesto/<id> Per-workstation view: live video, cycle-time history, current operator (if shown).├── /modulo/<name>/historico Per-module historical view: hours/days/weeks.├── /modulo/<name>/exportar Excel export trigger; produces `INDICADORES`-format .xlsx.├── /tiempos-perdidos Lost-time entry / coding (C001–C022). Replaces the manual end-of-day step.├── /camaras Camera health: feed up/down, last frame, model version, fps.├── /admin Settings: thresholds (`umbrales`), standard times, station↔operation mapping.├── /logs Live tail of journalctl-equivalent in browser.└── /salud Health page for monitoring (no auth needed if Tailscale-gated).Nav graph (high level)
Section titled “Nav graph (high level)” ┌───────────────────────┐ │ lbzfai.com / │ public marketing landing │ (current page) │ └──────────┬────────────┘ │ Auth0 login ▼ ┌───────────────────────┐ │ /app │ role-aware redirect └──────┬────────┬───────┘ executive│ │engineer / admin ▼ ▼ ┌─────────────────┐ ┌──────────────────────┐ │ /app/ejecutivo │ │ /app/ingeniero │ │ Mariana home │ │ Ronald landing │ │ ─ module tiles │ │ ─ "Open dashboard" │ │ ─ historico │ │ ─ Excel export │ └─────────────────┘ └──────────┬───────────┘ │ smart-redirect on Tailscale ▼ ┌───────────────────────────────┐ │ http://<jetson>:5000 │ │ Ronald's working dashboard │ └───────────────────────────────┘
(parallel) ┌──────────────────────┐ │ /app/investigacion │ ITBA view (read-only aggregate, no live video) └──────────────────────┘ ┌──────────────────────┐ │ /app/admin │ Sophia/Andrew └──────────────────────┘Route naming convention
Section titled “Route naming convention”- Public + executive surfaces: Spanish.
/entrar,/salir,/app/ejecutivo,/modulo/angela,/historico,/exportar. URLs are user-readable for Mariana. - Admin surfaces: English.
/app/admin. Internal-only. - Jetson surfaces: Spanish.
/modulo/<name>,/puesto/<id>,/tiempos-perdidos,/camaras,/admin. Ronald reads these. - Plurality: Spanish noun plurals —
/modulosfor index,/modulo/<name>for one. MatchINDICADORES-style vocabulary:puesto,modulo,operario,historico,eficiencia,umbrales.
Page-by-page intent (minimum spec, full UX in per-persona docs)
Section titled “Page-by-page intent (minimum spec, full UX in per-persona docs)”lbzfai.com pages
Section titled “lbzfai.com pages”| Route | Audience | Primary purpose | Auth gate |
|---|---|---|---|
/ | public + LBZF stakeholders | Marketing landing; “Sign in” CTA | none for view; click → Auth0 |
/app | any authenticated | Role-aware redirect dispatcher | required |
/app/ejecutivo | executive, admin | Mariana’s home: 4 module tiles + headline KPI | required + role |
/app/ejecutivo/modulo/<name> | executive, admin | Per-module executive drill-down (still aggregate, no per-operator detail) | required + role |
/app/ejecutivo/historico | executive, admin | Trend view: efficiency by day, week, month | required + role |
/app/ingeniero | engineer, admin | Ronald’s lbzfai.com home: “Open dashboard” + Excel + Tailscale instructions | required + role |
/app/ingeniero/abrir-jetson | engineer, admin | Smart redirect to Jetson local IP | required + role |
/app/ingeniero/exportar | engineer, admin | Excel export trigger; calls Jetson; downloads file | required + role |
/app/investigacion | research, admin | ITBA read-only aggregate view | required + role |
/app/admin | admin only | Sophia/Andrew: roles, audit log, deploy status | required + role |
/app/cuenta | any authenticated | Account: language, sign out, support contact | required |
/soporte | public | Static contact / support info | none |
Jetson :5000 pages
Section titled “Jetson :5000 pages”| Route | Audience | Primary purpose |
|---|---|---|
/ | engineer, admin | Ronald’s home (see ronald-dashboard-ux.md) |
/modulo/<name> | engineer, admin | Module live view: workstation grid + module-level pace |
/modulo/<name>/puesto/<id> | engineer, admin | Per-station: live video, cycle-time history, last 10 cycles |
/modulo/<name>/historico | engineer, admin | Historical trend at the module level |
/modulo/<name>/exportar | engineer, admin | Excel export endpoint — produces INDICADORES-format .xlsx |
/tiempos-perdidos | engineer, admin | C001–C022 lost-time coding |
/camaras | engineer, admin | Camera health |
/admin | admin | Thresholds, standard times, station↔operation mapping |
/logs | admin | Live log tail |
/salud | (none — health endpoint) | Monitoring health check, no auth |
How the two surfaces share auth
Section titled “How the two surfaces share auth”Open question: Does the Jetson :5000 dashboard re-authenticate against Auth0, or rely solely on Tailscale + a shared password?
Three options:
- Tailscale-only. If you’re on Tailscale, you’re in. Simplest. Loses per-user role enforcement on the Jetson — every Tailscale user has the same access.
- Auth0 on the Jetson too. Flask app integrates Auth0 OIDC. Needs DNS or a static URL the Auth0 tenant accepts as a callback — hard with
http://<tailscale-ip>:5000. Possibly possible withtailnet.<ts.net>hostnames. - Cloudflare Tunnel + Auth0 in front of the Jetson. Cloudflare Tunnel terminates a public hostname (
jetson.lbzfai.com) onto the Jetson, with Cloudflare Access enforcing Auth0. Eliminates the Tailscale dependency for legitimate users; the Jetson keeps its private IP. This is the strongest production answer if Cloudflare Tunnel proves acceptable — but it does mean LBZF data egresses through Cloudflare’s edge.
Proposed: Start with #1 for Phase I (Tailscale-only on Jetson) because it’s the v1.9 default and works for Sophia/Ronald immediately. Re-evaluate #3 for Phase II once the user count grows past 3 and audit logging becomes a requirement.
Owner of the call: Agent B + Sophia. This doc proposes #1; Agent B’s spec confirms or pushes to #3.
What the marketing landing (/) becomes
Section titled “What the marketing landing (/) becomes”Current page is generically captioned “Production Floor, in Real Time”. Refactor minimally for Phase I:
- Keep the hero, the eyebrow (“Louis Barton Zona Franca”), the lede.
- Add a small banner under the hero (Spanish): “Sistema interno de monitoreo de producción. Acceso autorizado únicamente.”
- The Sign in button now redirects post-login to
/app(the dispatcher). - After login, the home page either (a) auto-redirects to
/appor (b) shows a “Open dashboard” button. Prefer (a). - No changes to the deployment story (
output: 'static', Auth0 SPA hardcoded fallback, Cloudflare Workers with static assets).
Phase II / III page additions (sketch only)
Section titled “Phase II / III page additions (sketch only)”/app/ejecutivo/modulo/<name>becomes a stack — one route per of the 4 modules./app/operario/<id>— per-operator screen (only if operators ever log in, which they may not). Labor-law fence: see Agent E./app/comparar— module-vs-module benchmarking.- ITBA twin gets its own deployment with the same routes — see
auth0-roles-and-access.mdon tenant separation.
Alternatives considered
Section titled “Alternatives considered”- Option A — Tailscale-direct everywhere. Rejected: breaks Mariana on her phone. See render-location table.
- Option B — Cloudflare-proxied everything. Rejected for Phase I: significant new infra (Cloudflare Worker as Tailscale node, video proxying). Re-consider for Phase II.
- One single SPA at lbzfai.com that handles every page. Rejected: kills the static-output deploy story; introduces a JS bundle Astro doesn’t need; harder to gate marketing landing from auth.
- One Astro multi-page app + no Jetson dashboard at all (everything in Cloudflare). Rejected: the v1.9 plan and the data-locality story require keeping the operational dashboard local to the floor.
- English URL paths everywhere. Rejected for user-facing routes: Mariana and Ronald should be able to read the URL bar. Admin routes stay English.
Open questions
Section titled “Open questions”- OPEN: Render-location decision (Option A / B / C). Owner: Agent B, blocking: Phase I deploy, target: end of round-2 critique. This doc proposes C.
- CLOSED via ADR-005: Jetson auth model. Locked to Tailscale-only for Phase I. Auth0-on-Jetson and the outbound-tunnel proxy are parked in
60-parking/cloudflare-tunnel-from-jetson.md. - OPEN: Does Mariana install Tailscale, yes or no? Owner: Sophia + Mariana (via Armando), blocking: Option C viability, target: 2026-05-20.
- OPEN: Is the Excel export executed on the Jetson and downloaded through lbzfai.com, or only available from the Jetson dashboard? Owner: Agent B, blocking: Ronald’s UX flow, target: 2026-05-20. Current proposal: export endpoint on Jetson; lbzfai.com
exportarpage proxies the download. - OPEN: Should
/auto-redirect post-login users to/app, or land them on/with a banner? Owner: Sophia, blocking: nothing critical, target: 2026-05-13. - OPEN: Multi-tenant URL convention. If this becomes a SaaS, do tenants live at
<tenant>.lbzfai.com,lbzfai.com/<tenant>/..., or separate domains? Owner: Agent E (tenancy posture); seelbzfai-evolution-plan.md.
Cross-bucket dependencies
Section titled “Cross-bucket dependencies”- From backend/integration (Agent B): the render-location decision; the Excel-export endpoint contract; the JSON API surface for
/app/ejecutivo/*if Option C requires Cloudflare to call the Jetson. - From hardware/ML (Agent A, D): confirmation that per-workstation views are 1:1 with cameras (no overlapping FOV that would require a different page model).
- From business/cross-cutting (Agent E): Colombian labor-law constraints on what data ITBA can see → shapes
/app/investigacion. - From hardware (Agent A): Tailscale hostname/IP availability for the Jetson; this doc assumes a stable address.
What’s weak in this doc
Section titled “What’s weak in this doc”- Option C is a recommendation, not a decided architecture. If Agent B picks B (Cloudflare-proxied everything), the whole
lbzfai.comvs Jetson split collapses into one set of routes. The sitemap above survives more or less intact, but the data flow underneath changes substantially. - The Jetson :5000 routes are aspirational. No code exists yet. We do not actually know how Flask/FastAPI handles auth on a Tailscale-bound IP without a real domain.
- No latency budget per page. “Ronald glances at the dashboard” implies < 1s. We have not measured what that means in practice for any of the proposed pages.
- Per-station live-video proposed without a faces-vs-boxes decision. The /puesto/ page assumes a video feed is shown; that may be flatly illegal under Colombian labor law or unacceptable to operators. See
mariana-dashboard-ux.mdopen question; this doc inherits the same risk. - No accessibility analysis. We have not considered screen readers, color contrast for red/yellow/green efficiency badges, keyboard nav. Ronald is presumably sighted, but we don’t know his vision (and the population of future supervisors will vary).
- Browser support unspecified. Mariana’s phone could be a 2019 Android with an outdated Chrome — the IA assumes modern evergreen browsers without saying so.
Rollout
Section titled “Rollout”- Argentina demo (2026-05-13): show the sitemap diagram to Andrew and ITBA. Get sign-off on Option C (or pivot).
- By 2026-05-20: lbzfai.com page added:
/app/ingeniero(placeholder),/app/ejecutivo(placeholder, 4 module tiles, mocked data)./appdispatcher routes by Auth0 role. - By 2026-06-15: Jetson
:5000/and:5000/modulo/angelaworking against synthetic data. - By July 2026 deploy: full Option C operating against real Angela camera data.
- Phase II: add
/app/ejecutivo/modulo/<name>for Carmenza/Camisetas/Empaque; consider Cloudflare Tunnel migration. - Phase III: scale; re-evaluate Option B.
Appendix / references
Section titled “Appendix / references”~/lbzfai/src/pages/index.astro~/lbzfai/CLAUDE.md§“Architecture”docs/technical/architecture.mddocs/technical/deployment.md- v1.9 plan:
Full Hardware Rationale and Initial Checklist.docx(Drive10dE-XQZZb5h1ST7v6dkttxMPjWREluuP)