# Gigafibre FSM — Documentation

> **Start here.** Pick a row by intent, jump to the module index when you
> need scope, or go straight to the cross-module map when you're chasing a
> call that hops surfaces.

**Last refreshed:** 2026-04-22 (expanded module index, added dispatch /
customer portal / tech mobile docs, cross-interaction matrix)

---

## 1. Quick nav by intent

| I want to… | Open |
|---|---|
| **See the plan** — what's shipped, what's queued, with clickable links to every live module | [roadmap.md](roadmap.md) |
| **Understand the system end-to-end** — services, containers, networks, SSO, Traefik routes | [architecture/overview.md](architecture/overview.md) |
| **See who calls who** — one-page matrix of every module → module interaction | [architecture/module-interactions.md](architecture/module-interactions.md) |
| **Build a new feature** — UI patterns, folder layout, router conventions, Pinia, feature-sliced design | [architecture/app-design.md](architecture/app-design.md) |
| **Touch ERPNext data** — doctypes, customer → subscription → equipment → invoice, "Lead to Live" flow | [architecture/data-model.md](architecture/data-model.md) |
| **Work on dispatch** — drag-and-drop scheduling, tech assignment, travel-time optimization, magic-link SMS | [features/dispatch.md](features/dispatch.md) |
| **Work on the tech mobile app** — scan-to-identify, equipment install, job status, JWT auth | [features/tech-mobile.md](features/tech-mobile.md) |
| **Work on the customer portal** — passwordless magic-link, invoice + ticket self-service | [features/customer-portal.md](features/customer-portal.md) |
| **Work on billing, Stripe, payments** — subscription lifecycle, invoice OCR, payment reconciliation, PPA | [features/billing-payments.md](features/billing-payments.md) |
| **Touch CPE / modems / ONTs** — GenieACS, Oktopus, TR-069 → TR-369 migration, TP-Link XX230v diagnostics | [features/cpe-management.md](features/cpe-management.md) |
| **Build or debug the scanner / OCR** — Gemini vision pipeline, barcode/equipment/invoice endpoints, offline queue, AI_API_KEY rotation | [features/vision-ocr.md](features/vision-ocr.md) |
| **Work on agent flows / automation** — Flow Template model, trigger wiring, step editor, runtime contract | [features/flow-editor.md](features/flow-editor.md) |
| **Audit the wizard's SKU mapping vs legacy** | [reference/erpnext-item-diff.md](reference/erpnext-item-diff.md) |
| **Inspect the legacy PHP wizard** | [reference/legacy-wizard/](reference/legacy-wizard/) |
| **Read a historical status snapshot** | [archive/status-snapshots/](archive/status-snapshots/) |

---

## 2. Module index

Every deployed surface, by app. Each row links to its primary doc and
names the exact code paths so you can `grep` from here.

### 2a. Ops SPA (`apps/ops/`) — `https://erp.gigafibre.ca/ops/`

**Single pane of glass for internal teams.** Quasar PWA (SPA mode in dev,
PWA mode in prod), Vue 3 + Pinia, hash router. Wrapped by Authentik via
Traefik `forwardAuth` on `/ops/*`. Bottom of the same app hosts the tech
mobile module on `/j/*` with magic-link auth.

| Module | URL | Purpose | Primary doc | Primary code |
|---|---|---|---|---|
| **Dashboard** | `/ops/#/` | KPIs (MRR, AR, active tickets), dispatch summary, live SSE feed | — | `src/pages/DashboardPage.vue` |
| **Clients** | `/ops/#/clients` · `/clients/:id` | Customer 360: profile, subscriptions, invoices, equipment, tickets, communication timeline | — | `src/pages/ClientsPage.vue` · `ClientDetailPage.vue` · `src/modules/clients/` |
| **Dispatch** | `/ops/#/dispatch` | Drag-and-drop tech schedule, job board, travel-time optimization, SMS magic-link issuance | [features/dispatch.md](features/dispatch.md) | `src/pages/DispatchPage.vue` · `src/modules/dispatch/` |
| **Tickets** | `/ops/#/tickets` | Support ticket board, SLA clock, linked equipment + jobs | — | `src/pages/TicketsPage.vue` · `src/modules/tickets/` |
| **Équipe** | `/ops/#/equipe` | Technician roster, skills/tags, availability, today's jobs | [features/dispatch.md §6](features/dispatch.md) | `src/pages/EquipePage.vue` · `src/modules/equipe/` |
| **Rapports** | `/ops/#/rapports` + `/revenus` · `/ventes` · `/taxes` · `/ar` | Revenue, sales, tax, AR reports (renders in-browser; PDF export via hub) | [features/billing-payments.md §9](features/billing-payments.md) | `src/pages/Rapports*.vue` · `src/modules/rapports/` |
| **OCR** | `/ops/#/ocr` | Upload scanned supplier bills → Gemini parse → pre-fill Purchase Invoice | [features/vision-ocr.md §4](features/vision-ocr.md) | `src/pages/OcrPage.vue` |
| **Settings** | `/ops/#/settings` | Feature flags, integration keys, user management (mirror of ERPNext) | — | `src/pages/SettingsPage.vue` |
| **Telephony** | `/ops/#/telephony` | SIP click-to-dial (Twilio Voice SDK + sip.js fallback), call history | [architecture/overview.md §3](architecture/overview.md) | `src/pages/TelephonyPage.vue` |
| **Agent Flows** | `/ops/#/agent-flows` | Visual editor for Flow Template (automation runtime) | [features/flow-editor.md](features/flow-editor.md) | `src/pages/AgentFlowsPage.vue` |
| **Network** | `/ops/#/network` | Cytoscape-rendered OLT/ONT topology, live link status from GenieACS | [features/cpe-management.md](features/cpe-management.md) | `src/pages/NetworkPage.vue` |

### 2b. Ops tech module (`apps/ops/src/modules/tech/`) — `https://erp.gigafibre.ca/ops/#/j/*`

**Same SPA, different mount.** Techs hit a magic-link SMS, land on `/j/:token`,
the token redirects to `/j` with JWT in localStorage. Mobile-first layout
with bottom tab bar.

| Route | Purpose | Primary doc | Primary code |
|---|---|---|---|
| `/j` | Today's jobs, grouped by status | [features/tech-mobile.md §4a](features/tech-mobile.md) | `TechTasksPage.vue` |
| `/j/job/:name` | Job detail + equipment management (Ajouter/Scanner/Rechercher/Créer) | [features/tech-mobile.md §5](features/tech-mobile.md) | `TechJobDetailPage.vue` |
| `/j/scan` | Standalone scanner (native camera → Gemini) | [features/tech-mobile.md §3](features/tech-mobile.md) | `TechScanPage.vue` |
| `/j/device/:serial` | Per-device detail, diagnostic shortcut | — | `TechDevicePage.vue` |
| `/j/diagnostic` | In-browser speed test + GenieACS probe | [features/cpe-management.md](features/cpe-management.md) | `TechDiagnosticPage.vue` |
| `/j/more` | Settings, token expiry, logout | — | `TechMorePage.vue` |
| `/j/:token` | Magic-link handler (last route, accepts any JWT) | [features/tech-mobile.md §2](features/tech-mobile.md) | `TechTasksPage.vue` (same component, different entry) |

### 2c. Field PWA (`apps/field/`) — `https://erp.gigafibre.ca/field/`

**Transitional standalone PWA, retiring.** Authentik-protected; byte-for-byte
closer to the legacy workflow techs had before the Ops tech module
existed. No magic-link — tech logs in via Authentik. Kept alive while the
SMS link still points at the hub SSR page (see tech-mobile doc §1).

Same page set as §2b (TasksPage, ScanPage, JobDetailPage, DevicePage,
DiagnosticPage, MorePage), same Gemini-native scanner composable.
`apps/field/src/pages/JobDetailPage.vue` is the reference implementation
we port *from* when the Ops tech module drifts.

### 2d. Customer portal (`apps/client/`) — `https://portal.gigafibre.ca/`

**Passwordless self-service.** Standalone Quasar PWA served by a dedicated
nginx container (`client-portal`) bind-mounted from `/opt/client-app/` on
the Proxmox VM. No Authentik — customers get a magic-link email (HS256
JWT, 24h TTL).

| Module | URL | Purpose | Primary doc | Primary code |
|---|---|---|---|---|
| **Login** | `/#/login` | Email entry → magic-link email send | [features/customer-portal.md §3](features/customer-portal.md) | `src/pages/LoginPage.vue` |
| **Dashboard** | `/#/` | Balance, next invoice, active tickets, recent activity | [features/customer-portal.md §4a](features/customer-portal.md) | `DashboardPage.vue` |
| **Account** | `/#/account` | Profile, saved payment methods (Stripe PM), address book | [features/billing-payments.md §6](features/billing-payments.md) | `AccountPage.vue` |
| **Catalog / Cart** | `/#/catalog` · `/#/cart` | Shop add-ons (extra mesh, static IP), add to Stripe Checkout | [features/billing-payments.md](features/billing-payments.md) | `CatalogPage.vue` · `CartPage.vue` |
| **Invoices** | `/#/invoices` · `/#/invoices/:name` | List + PDF of all Sales Invoices, Stripe-linked pay button | [features/billing-payments.md §8](features/billing-payments.md) | `InvoicesPage.vue` · `InvoiceDetailPage.vue` |
| **Tickets** | `/#/tickets` · `/#/tickets/:name` | Create support ticket, reply thread, attach photos | — | `TicketsPage.vue` · `TicketDetailPage.vue` |
| **Messages** | `/#/messages` | Unified comm thread (SMS + email) with support | — | `MessagesPage.vue` |
| **Payment flows** | `/#/payment-success` · `/payment-cancel` · `/payment-card-added` · `/order-success` | Stripe return URL landings | [features/billing-payments.md §7](features/billing-payments.md) | `Payment*.vue` · `OrderSuccessPage.vue` |

### 2e. Legacy portal (`apps/portal/`) — **retired 2026-04-22**

Old ERPNext-embedded customer portal. Host `client.gigafibre.ca` now 307s
to `portal.gigafibre.ca/*` via Traefik; see
`apps/portal/traefik-client-portal.yml`. Codebase retained as historical
reference only. Do not build.

### 2f. Marketing site (`apps/website/`) — `https://www.gigafibre.ca/`

React + Vite marketing site. Self-contained, no ERPNext dependency,
no docs beyond its own README.

---

## 3. Services (backends)

Everything in `services/*` is backend infrastructure. Usually you don't
edit these unless you're adding a new endpoint.

| Service | Port | Role | Primary docs |
|---|---|---|---|
| `services/targo-hub/` | `msg.gigafibre.ca:3300` | **API gateway.** Vision (Gemini), magic-link JWT, Twilio SMS, SSE fanout, Stripe webhooks, ACS proxy, Legacy-DB read proxy, tech-mobile SSR | [overview.md §3](architecture/overview.md) · [vision-ocr.md](features/vision-ocr.md) · [customer-portal.md](features/customer-portal.md) · [tech-mobile.md](features/tech-mobile.md) · [billing-payments.md](features/billing-payments.md) |
| `services/modem-bridge/` | `:3301` (internal) | Playwright + Chromium wrapper that logs into TP-Link XX230v modems and reads encrypted TR-181 data | [cpe-management.md](features/cpe-management.md) |
| `services/docuseal/` | `docs.gigafibre.ca:3000` | Contract signing for commercial quotes + residential service agreements | [flow-editor.md §5](features/flow-editor.md) (briefly) |
| `services/legacy-db/` | `:3305` (internal) | Read-only proxy over the retired MariaDB for lookups that haven't migrated | [architecture/overview.md](architecture/overview.md) |

**ERPNext** (`erp.gigafibre.ca`) is not under `services/` — it's a standard
Frappe-Bench install on the VM. See
[architecture/data-model.md](architecture/data-model.md) for the doctype
catalog.

---

## 4. Cross-module interactions (one-page map)

> Full matrix + sequence diagrams in
> [architecture/module-interactions.md](architecture/module-interactions.md).
> The summary below is just the high-traffic edges.

```text
                    ┌───────────────────────────────────────┐
                    │         ERPNext (source of truth)     │
                    │     Customer · Subscription ·         │
                    │     Equipment · Dispatch Job ·        │
                    │     Sales Invoice · Ticket            │
                    └──┬──────────────┬──────────┬──────────┘
                       │              │          │
           REST+token  │      REST+token         │ REST+token
                       │              │          │
          ┌────────────▼──┐   ┌───────▼────────┐ │
          │  targo-hub    │   │   Ops SPA      │ │
          │  (gateway)    │◄──┤   apps/ops/    │ │
          │               │   └────┬───────────┘ │
          │ /vision/*     │        │             │
          │ /magic-link/* │        │ bundles     │
          │ /sse          │        │ tech module │
          │ /sms          │        │ at /j/*     │
          │ /stripe/*     │        │             │
          │ /acs/*        │        └─────────────┘
          │ /t/{token}    │                      │
          └──┬──────┬─────┘                      │
             │      │                            │
       SMS   │      │ email (magic link)         │
       /SSE  │      │                            │
             ▼      ▼                            ▼
     ┌─────────────────┐    ┌────────────────────────┐
     │ Tech mobile     │    │ Customer portal        │
     │ (SSR OR Vue)    │    │ apps/client/           │
     │ /t or /ops/#/j/ │    │ portal.gigafibre.ca    │
     └─────────────────┘    └────────────────────────┘
```

**Highest-traffic edges (read these docs first if you're chasing one):**

| From | To | Via | Where it's documented |
|---|---|---|---|
| Dispatch (Ops) | Tech mobile | Magic-link SMS → hub/magic-link.js → Twilio | [dispatch.md §5](features/dispatch.md) + [tech-mobile.md §2](features/tech-mobile.md) |
| Tech mobile scanner | Gemini | POST hub `/vision/barcodes` + `/vision/equipment` | [vision-ocr.md §2](features/vision-ocr.md) + [tech-mobile.md §3](features/tech-mobile.md) |
| Ops OCR page | Gemini | POST hub `/vision/invoice` | [vision-ocr.md §3](features/vision-ocr.md) |
| Customer portal | ERPNext | Authenticated via customer JWT → hub proxies `/api/resource/*` | [customer-portal.md §6](features/customer-portal.md) |
| Stripe | ERPNext | Webhook → hub `/stripe/webhook` → creates Payment Entry | [billing-payments.md §7](features/billing-payments.md) |
| Ops dispatch | SSE live updates | hub `/sse` (tech status, job flip, ETA) | [dispatch.md §4](features/dispatch.md) |
| Agent-flows | Any module | Runtime in hub → triggers any ERPNext op via service token | [flow-editor.md §6](features/flow-editor.md) |
| GenieACS | Modem-bridge | hub `/acs/probe` → modem-bridge for TP-Link deep params | [cpe-management.md §4](features/cpe-management.md) |

---

## 5. Folder map

```text
docs/
├── README.md                    ← you are here
├── roadmap.md                   ← phase tracker + live module URLs
│
├── architecture/                ← the system itself
│   ├── overview.md              (services, Docker, Traefik, SSO, retirement plan)
│   ├── data-model.md            (ERPNext doctypes + "Lead to Live")
│   ├── app-design.md            (Vue/Quasar patterns, feature-sliced layout)
│   └── module-interactions.md   (full call graph + sequence diagrams)
│
├── features/                    ← one doc per business capability
│   ├── README.md                (per-doc index)
│   ├── dispatch.md              (Ops dispatch board, scheduling, tags, SMS)
│   ├── tech-mobile.md           (Gemini-native scanner, equipment UX, JWT)
│   ├── customer-portal.md       (Plan A magic-link, Stripe self-service)
│   ├── billing-payments.md      (Stripe, invoices, subscriptions, PPA)
│   ├── cpe-management.md        (GenieACS, Oktopus, XX230v, modem-bridge)
│   ├── vision-ocr.md            (Gemini, scan pipeline, offline queue, keys)
│   └── flow-editor.md           (Flow Template, triggers, runtime)
│
├── reference/                   ← lookup material
│   ├── erpnext-item-diff.md     (new SKUs vs legacy gestionclient)
│   └── legacy-wizard/           (read-only snapshot of the PHP wizard)
│
├── assets/                      ← PPTX decks, screenshots, generated diagrams
│
└── archive/                     ← frozen docs, do not edit
    ├── HANDOFF-2026-04-18.md
    ├── MIGRATION.md
    ├── LEGACY-ACCOUNTING-ANALYSIS.md
    └── status-snapshots/        (dated session reports)
```

---

## 6. Conventions

- **Edit in place.** Don't copy-paste a section into a new file — link to it instead.
- **Module index = source of truth for URLs.** If you change a route or a
  deployed host, update §2 of this file *and* the relevant feature doc.
  `grep -r "erp.gigafibre.ca" docs/` should not return stale hostnames.
- **Cross-doc links use repo-relative paths,** e.g.
  `[features/dispatch.md](features/dispatch.md)` — so `git mv`ing a doc
  surfaces every incoming link.
- **Code comments that reference a doc** use the full repo-relative path,
  e.g. `// See docs/features/vision-ocr.md §10` — so `grep docs/` still
  surfaces them if we reorganize again.
- **Dated snapshots go to `archive/status-snapshots/`.** Don't create
  `STATUS_YYYY-MM-DD.md` at the root anymore.
- **The root `README.md`** (one level up) is the repo introduction for
  non-technical readers; `docs/README.md` (this file) is the engineering
  index.

---

## 7. What changed on 2026-04-22

Big reorganization:

1. **New feature docs** added: `dispatch.md`, `customer-portal.md`,
   `tech-mobile.md`. The first was missing entirely despite being the
   most-used surface in Ops; the second codifies the Plan A magic-link
   cutover; the third replaces a stale `tech-mobile.md` that incorrectly
   referenced `html5-qrcode` (never shipped — scanner has always been
   native camera + Gemini).
2. **New architecture doc** added: `module-interactions.md` — the
   one-page call graph with sequence diagrams for the hot paths (dispatch
   → SMS → tech scan → equipment install).
3. **Module index** (§2 above) now lists *every* deployed surface, not
   just the three that had feature docs. Dispatch, Tickets, Équipe,
   Rapports, Telephony, Network, Agent Flows, OCR, and every customer-portal
   page now have a named row.
4. **Customer portal topology clarified.** `portal.gigafibre.ca` is the
   live SPA; `client.gigafibre.ca` 307s to portal. The legacy
   `apps/portal/` tree is retained as historical reference only.
5. **Earlier flattening (still in effect):** docs were flattened out of a
   single folder into `architecture/`, `features/`, `reference/`,
   `archive/`. Every file move used `git mv` so `git log --follow` still
   works. The old paths (`docs/ARCHITECTURE.md`, `docs/VISION_AND_OCR.md`,
   etc.) no longer exist — if you land on one of those links from an
   external bookmark, use §1 above to find the new location. Legacy
   references remain only inside `archive/HANDOFF-2026-04-18.md` and
   `archive/status-snapshots/*.md`, where they're preserved as a frozen
   historical record.
