docs: bring all docs in sync with the May 2026 reality
Mass refresh — the docs were last touched 2026-04-22, two weeks behind
shipped reality. This commit updates 9 files to reflect current truth.
WHAT CHANGED IN THE PRODUCT (since 22 Apr) THAT THE DOCS NOW REFLECT:
• Oktopus CE / TR-369 stack decommissioned (containers + volumes +
images all removed; broker had filled /dev/sdb with 75 GB of debug
logs and took ERPNext down for 4 days). Hub gates the integration
behind OKTOPUS_DISABLED=1 — modules retained, no-op'd at runtime.
• dispatch.gigafibre.ca (legacy PHP SPA) replaced by an nginx 301
redirect to /ops/#/dispatch.
• Top toolbar of the dispatch module: collapsed to single-color
Lucide icons + ⋯ overflow menu + "Vue principale ▾" + "[👥 N ▾]"
resource type chip (defaults to techs, materials in the dropdown
only when relevant).
• Tech home base / departure point: editable per-tech via 📍 button,
address geocode (Nominatim) or click-on-map picker, right-click
on tech pin opens the same actions. Map defaults centered on
Gigafibre HQ (1867 chemin de la Rivière, Sainte-Clotilde) instead
of downtown Montreal.
• POST /auth/users invite flow on the hub: creates the Authentik
user, sets a temp password, mails it via Mailjet (Authentik's
own recovery flow isn't configured), creates the matching ERPNext
System User. Surfaced in ops Settings → Utilisateurs → Inviter.
• Two Authentik instances clarified as parallel-and-permanent (not
a migration): auth.targo.ca for staff, id.gigafibre.ca for clients.
FILES TOUCHED:
README.md — service table refreshed, arch diagram redrawn (no
Oktopus row), auth section explains the invite flow + two
parallel instances.
docs/architecture/overview.md — new "Decommissioned" section,
correct retirement status for dispatch-app + apps/field, two
Authentik instances explicitly distinguished, dev-gotchas list
rewritten (drops MongoDB AVX, adds log-rotation hard-learned
lesson, adds note about Authentik recovery flow).
docs/architecture/data-model.md — Step 5 hardware provisioning
now describes the GenieACS path (TR-069 Inform → preset push)
instead of the dead TR-369 path.
docs/architecture/module-interactions.md — oktopus.js and
oktopus-mqtt.js entries marked as gated, provision.js note
updated, GenieACS row in external-integrations updated, MQTT
row removed from real-time channels, interaction matrix loses
the Oktopus column and gains an Authentik admin REST cell.
docs/features/dispatch.md — Top bar section completely rewritten
to match the current chrome (left/center/right regions,
single-color Lucide, dropdowns); new Tech home base section
documenting the 📍 + map-pick + right-click flows; retirement
note now reads as a status, not a plan.
docs/features/cpe-management.md — full rewrite. Oktopus migration
plan replaced by a "decommissioned" note + the existing GenieACS
+ modem-bridge architecture as the steady state. TP-Link XX230v
deep-dive sections preserved (still accurate).
docs/README.md, docs/features/README.md, docs/roadmap.md —
intent-table descriptions and live-URLs table corrected.
The docs/archive/ snapshots (2026-04-18, 2026-04-19) are untouched —
they're historical and should remain that way.
This commit is contained in:
parent
cbeb61e04e
commit
0f8d2b0565
56
README.md
56
README.md
|
|
@ -37,34 +37,38 @@ gigafibre-fsm/
|
||||||
|
|
|
|
||||||
+----------+----------+----------+----------+----------+
|
+----------+----------+----------+----------+----------+
|
||||||
| | | | | |
|
| | | | | |
|
||||||
ERPNext Ops PWA Authentik n8n Website Oktopus
|
ERPNext Ops PWA Authentik n8n Website DocuSeal
|
||||||
erp. erp. auth. n8n. www. oss.
|
erp. erp. auth. n8n. www. docs.
|
||||||
gigafibre gigafibre targo.ca giga giga giga
|
gigafibre gigafibre targo.ca giga giga gigafibre
|
||||||
.ca .ca/ops/ fibre fibre fibre
|
.ca .ca/ops/ id.giga fibre fibre .ca
|
||||||
.ca .ca .ca
|
fibre.ca .ca .ca
|
||||||
|
|
|
|
||||||
targo-hub (API gateway)
|
targo-hub (API gateway, msg.gigafibre.ca)
|
||||||
|
|
|
|
||||||
+----------+----------+----------+
|
+----------+----------+----------+----------+
|
||||||
| | | |
|
| | | | |
|
||||||
GenieACS Twilio Traccar modem-bridge
|
GenieACS Twilio Mailjet Stripe Traccar
|
||||||
(TR-069) (SMS) (GPS) (SNMP/TR-069)
|
(TR-069) (SMS) (email) ($) (GPS)
|
||||||
|
|
|
||||||
|
modem-bridge (SNMP / TR-069 deep-dive for TP-Link)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Services & Dependencies
|
## Services & Dependencies
|
||||||
|
|
||||||
| Service | URL | Port | Stack | Purpose |
|
| Service | URL | Stack | Purpose |
|
||||||
|---------|-----|------|-------|---------|
|
|---------|-----|-------|---------|
|
||||||
| ERPNext | erp.gigafibre.ca | 8080 | Frappe v16, PostgreSQL | ERP backend, API |
|
| ERPNext | erp.gigafibre.ca | Frappe v16, PostgreSQL | ERP / billing / ticketing — Source of Truth |
|
||||||
| Ops PWA | erp.gigafibre.ca/ops/ | 80 | Vue 3, Quasar, Pinia | Staff operations app |
|
| Ops SPA | erp.gigafibre.ca/ops/ | Vue 3, Quasar v2, Pinia | Internal operations app (dispatch, clients, settings) |
|
||||||
| targo-hub | internal | 3100 | Node.js, Express | API gateway to external services |
|
| targo-hub | msg.gigafibre.ca | Node.js 20, Express | API gateway: SMS, SSE, AI, OAuth admin, Stripe webhooks, Traccar proxy |
|
||||||
| modem-bridge | internal | 3200 | Node.js | SNMP/TR-069 CPE diagnostics |
|
| modem-bridge | internal | Node.js, Playwright | TR-181 deep-dive for TP-Link XX230v / Deco mesh |
|
||||||
| Authentik | auth.targo.ca / id.gigafibre.ca | 9000 | Python, PostgreSQL | SSO (staff + customers) |
|
| Authentik (staff) | auth.targo.ca | Python, PostgreSQL | SSO for ops + n8n + Gitea + ERPNext OAuth provider |
|
||||||
| n8n | n8n.gigafibre.ca | 5678 | Node.js | Workflow automation (SMS, email) |
|
| Authentik (clients) | id.gigafibre.ca | Python, PostgreSQL | SSO for customer-facing portals |
|
||||||
| Traefik | -- | 80/443 | Go | Reverse proxy, TLS, forwardAuth |
|
| n8n | n8n.gigafibre.ca | Node.js | Workflow automation (campaigns, SMS, email) |
|
||||||
| Oktopus | oss.gigafibre.ca | 8428 | Go | TR-069 CPE management |
|
| Traefik | — | Go | Reverse proxy, TLS, forwardAuth middleware |
|
||||||
| Website | www.gigafibre.ca | 80 | React, Vite, Tailwind | Marketing site + address API |
|
| Website | www.gigafibre.ca | React, Vite, Tailwind | Marketing site + address API |
|
||||||
| Traccar | tracker.targointernet.com | 8082 | Java | GPS tracking for techs |
|
| DocuSeal | docs.gigafibre.ca | Ruby on Rails | Contract e-signature |
|
||||||
|
| GenieACS | (external) | Node.js, MongoDB | TR-069 ACS for ONT/router fleet |
|
||||||
|
| Traccar | (external) | Java | GPS tracking for techs |
|
||||||
|
|
||||||
## ERPNext Custom Doctypes
|
## ERPNext Custom Doctypes
|
||||||
|
|
||||||
|
|
@ -124,7 +128,13 @@ cd apps/ops && bash deploy.sh
|
||||||
|
|
||||||
## Auth Pattern
|
## Auth Pattern
|
||||||
|
|
||||||
Authentik SSO protects staff apps via Traefik `forwardAuth`. The ops app reads `X-Authentik-Email` from the proxied request header. All ERPNext API calls from targo-hub and the ops nginx proxy use `Authorization: token <ERP_SERVICE_TOKEN>` (Bearer token from server `.env`). Customer-facing SSO is at id.gigafibre.ca, federated from auth.targo.ca.
|
Two parallel Authentik instances — **not** a migration in progress:
|
||||||
|
- **`auth.targo.ca`** — staff-facing. Protects `/ops/`, n8n, Gitea, the hub admin endpoints. Also acts as the OAuth provider for ERPNext sign-in. Lives on the prod box at `/opt/authentik` (managed by Targo's IT team).
|
||||||
|
- **`id.gigafibre.ca`** — customer-facing. Protects the client portal. Lives at `/opt/authentik-client` on the prod box.
|
||||||
|
|
||||||
|
Staff apps go through Traefik `forwardAuth` → Authentik outpost → cookie. The ops SPA reads `X-Authentik-Email` from the proxied header to identify the user, then maps Authentik groups to the in-app capability set (`useUserGroups.js`). All ERPNext API calls from targo-hub use `Authorization: token <ERP_SERVICE_TOKEN>` (Bearer from `.env`).
|
||||||
|
|
||||||
|
New users are added via **ops Settings → Utilisateurs → "Inviter"** which hits `POST /auth/users` on the hub. The hub creates the Authentik user, sets a temporary password, emails it via Mailjet, and creates the matching ERPNext System User in one round-trip. See [docs/SETUP.md](docs/SETUP.md) §6.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ customer portal / tech mobile docs, cross-interaction matrix)
|
||||||
| **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 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 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) |
|
| **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) |
|
| **Touch CPE / modems / ONTs** — GenieACS (TR-069), modem-bridge for TP-Link XX230v deep diagnostics, diagnostic-swap workflow | [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) |
|
| **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) |
|
| **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) |
|
| **Audit the wizard's SKU mapping vs legacy** | [reference/erpnext-item-diff.md](reference/erpnext-item-diff.md) |
|
||||||
|
|
@ -209,7 +209,7 @@ docs/
|
||||||
│ ├── tech-mobile.md (Gemini-native scanner, equipment UX, JWT)
|
│ ├── tech-mobile.md (Gemini-native scanner, equipment UX, JWT)
|
||||||
│ ├── customer-portal.md (Plan A magic-link, Stripe self-service)
|
│ ├── customer-portal.md (Plan A magic-link, Stripe self-service)
|
||||||
│ ├── billing-payments.md (Stripe, invoices, subscriptions, PPA)
|
│ ├── billing-payments.md (Stripe, invoices, subscriptions, PPA)
|
||||||
│ ├── cpe-management.md (GenieACS, Oktopus, XX230v, modem-bridge)
|
│ ├── cpe-management.md (GenieACS, XX230v, modem-bridge, diagnostic-swap)
|
||||||
│ ├── vision-ocr.md (Gemini, scan pipeline, offline queue, keys)
|
│ ├── vision-ocr.md (Gemini, scan pipeline, offline queue, keys)
|
||||||
│ └── flow-editor.md (Flow Template, triggers, runtime)
|
│ └── flow-editor.md (Flow Template, triggers, runtime)
|
||||||
│
|
│
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,9 @@ Through `targo-hub` or `n8n`, a successful sign-up generates exactly:
|
||||||
|
|
||||||
### Step 5: Hardware Auto-Provisioning
|
### Step 5: Hardware Auto-Provisioning
|
||||||
1. When the order is generated, custom fields on `Service Equipment` (like `custom_wifi_ssid` and `custom_sip_username`) are prefilled in ERPNext.
|
1. When the order is generated, custom fields on `Service Equipment` (like `custom_wifi_ssid` and `custom_sip_username`) are prefilled in ERPNext.
|
||||||
2. The technician arrives, installs the TP-Link ONT, and scans its serial/MAC.
|
2. The technician arrives, installs the TP-Link ONT, and scans its serial/MAC via the tech mobile page.
|
||||||
3. The ONT boots up and hits the TR-369 server (Oktopus).
|
3. The ONT boots up and reaches the GenieACS ACS over TR-069 / CWMP for its first Inform.
|
||||||
4. `targo-hub` acts as the TR-069 proxy, matching the MAC to ERPNext, reading the custom provisioning profile, and automatically injecting the WiFi and SIP credentials onto the modem.
|
4. `targo-hub` matches the MAC to the ERPNext Service Equipment row, fetches the custom provisioning profile, and pushes a GenieACS preset that injects the WiFi and SIP credentials onto the modem on its next Inform cycle.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ module or moving an existing call, update this file first.
|
||||||
|
|
||||||
| Module | Host / Port | Owns | Notes |
|
| Module | Host / Port | Owns | Notes |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `services/targo-hub` | `msg.gigafibre.ca:3300` | API gateway / monolith. Routes registered in [`services/targo-hub/server.js`](../../services/targo-hub/server.js). Every external integration (Twilio, Stripe, Mailjet, Gemini, GenieACS, Oktopus, Traccar) is reached *through* the hub — frontends never hit those directly. | Lib catalog below. |
|
| `services/targo-hub` | `msg.gigafibre.ca:3300` | API gateway / monolith. Routes registered in [`services/targo-hub/server.js`](../../services/targo-hub/server.js). Every external integration (Twilio, Stripe, Mailjet, Gemini, GenieACS, Traccar, DocuSeal, Authentik admin) is reached *through* the hub — frontends never hit those directly. | Lib catalog below. |
|
||||||
| `services/modem-bridge` | `:3301` (internal) | Playwright + headless Chromium driving TP-Link XX230v web UI to read encrypted TR-181 parameters the vendor exposes only via client-side JS. Token auth, internal network only. | [`services/modem-bridge/server.js`](../../services/modem-bridge/server.js) |
|
| `services/modem-bridge` | `:3301` (internal) | Playwright + headless Chromium driving TP-Link XX230v web UI to read encrypted TR-181 parameters the vendor exposes only via client-side JS. Token auth, internal network only. | [`services/modem-bridge/server.js`](../../services/modem-bridge/server.js) |
|
||||||
| `services/docuseal` | `sign.gigafibre.ca` | DocuSeal container for commercial-contract e-signature. Residential contracts use a JWT-based acceptance flow (see `lib/acceptance.js` + `lib/contracts.js`) — DocuSeal is the commercial track. | — |
|
| `services/docuseal` | `sign.gigafibre.ca` | DocuSeal container for commercial-contract e-signature. Residential contracts use a JWT-based acceptance flow (see `lib/acceptance.js` + `lib/contracts.js`) — DocuSeal is the commercial track. | — |
|
||||||
| `services/legacy-db` | `10.100.80.100:3307` | Read-only MariaDB bridge into the old PHP `gestionclient` database. Used by migration scripts and a handful of lookup queries in `lib/auth.js`. | [`services/legacy-db/docker-compose.yml`](../../services/legacy-db/docker-compose.yml) |
|
| `services/legacy-db` | `10.100.80.100:3307` | Read-only MariaDB bridge into the old PHP `gestionclient` database. Used by migration scripts and a handful of lookup queries in `lib/auth.js`. | [`services/legacy-db/docker-compose.yml`](../../services/legacy-db/docker-compose.yml) |
|
||||||
|
|
@ -75,8 +75,7 @@ One file per capability; the router in `server.js` dispatches by path prefix.
|
||||||
| `magic-link.js` | `/magic-link*` | JWT sign/verify + one-time magic-link issuance. |
|
| `magic-link.js` | `/magic-link*` | JWT sign/verify + one-time magic-link issuance. |
|
||||||
| `modem-bridge.js` | `/modem*` | Thin client for the modem-bridge sidecar. |
|
| `modem-bridge.js` | `/modem*` | Thin client for the modem-bridge sidecar. |
|
||||||
| `network-intel.js` | `/network/*` | InfluxDB + Grafana log analysis → Gemini summarization. |
|
| `network-intel.js` | `/network/*` | InfluxDB + Grafana log analysis → Gemini summarization. |
|
||||||
| `oktopus-mqtt.js` | (worker) | MQTT subscriber to the Oktopus broker — compensates for their broken events-controller hook. |
|
| `oktopus.js` / `oktopus-mqtt.js` | gated | TR-369/USP integration — disabled May 2026 (`OKTOPUS_DISABLED=1` in hub env). Files retained behind the env gate; if the gate is flipped, registers at `/oktopus/*` and starts the MQTT subscriber worker. |
|
||||||
| `oktopus.js` | `/oktopus/*` | Oktopus CE REST API — TR-369/USP device preauth and provisioning. |
|
|
||||||
| `olt-snmp.js` | `/olt*` | SNMP poller for ONU status / optical power on the OLTs. |
|
| `olt-snmp.js` | `/olt*` | SNMP poller for ONU status / optical power on the OLTs. |
|
||||||
| `otp.js` | (lib) | One-time-password issuance + email/SMS delivery (used by checkout + portal). |
|
| `otp.js` | (lib) | One-time-password issuance + email/SMS delivery (used by checkout + portal). |
|
||||||
| `outage-monitor.js` | `/webhook/kuma` | Uptime-Kuma webhook ingest; cross-references OLT poller to synthesize mass-outage alerts. |
|
| `outage-monitor.js` | `/webhook/kuma` | Uptime-Kuma webhook ingest; cross-references OLT poller to synthesize mass-outage alerts. |
|
||||||
|
|
@ -84,7 +83,7 @@ One file per capability; the router in `server.js` dispatches by path prefix.
|
||||||
| `pbx.js` | `/webhook/3cx/call-event` | 3CX call-event webhook → ERPNext Communication. |
|
| `pbx.js` | `/webhook/3cx/call-event` | 3CX call-event webhook → ERPNext Communication. |
|
||||||
| `portal-auth.js` | `/portal/*` | Passwordless magic-link endpoint `POST /portal/request-link` — rate-limited, anti-enumeration. |
|
| `portal-auth.js` | `/portal/*` | Passwordless magic-link endpoint `POST /portal/request-link` — rate-limited, anti-enumeration. |
|
||||||
| `project-templates.js` | (lib) | Hard-coded job-template catalogue (fibre_install, etc.) for dispatch. |
|
| `project-templates.js` | (lib) | Hard-coded job-template catalogue (fibre_install, etc.) for dispatch. |
|
||||||
| `provision.js` | `/provision/*` | Device provisioning orchestrator — GenieACS preset push + Oktopus preauth. |
|
| `provision.js` | `/provision/*` | Device provisioning orchestrator — GenieACS preset push (Oktopus pre-auth call retained but no-ops while `OKTOPUS_DISABLED=1`). |
|
||||||
| `referral.js` | `/api/referral/*` | Referral credit validation + application, backing the wizard. |
|
| `referral.js` | `/api/referral/*` | Referral credit validation + application, backing the wizard. |
|
||||||
| `reports.js` | `/reports*` | GL / revenue / tax / A/R reports, Frappe PostgreSQL queries. |
|
| `reports.js` | `/reports*` | GL / revenue / tax / A/R reports, Frappe PostgreSQL queries. |
|
||||||
| `sse.js` | `/sse`, `/broadcast` | Server-Sent Events fan-out. Topics: `customer:*`, `conversations`, `dispatch`, etc. |
|
| `sse.js` | `/sse`, `/broadcast` | Server-Sent Events fan-out. Topics: `customer:*`, `conversations`, `dispatch`, etc. |
|
||||||
|
|
@ -105,8 +104,7 @@ One file per capability; the router in `server.js` dispatches by path prefix.
|
||||||
| Twilio | `lib/twilio.js`, `lib/voice-agent.js` | SMS / Voice (REST + webhook + Media Streams WS) | SMS magic-links, OTP, IVR, Voice tokens. |
|
| Twilio | `lib/twilio.js`, `lib/voice-agent.js` | SMS / Voice (REST + webhook + Media Streams WS) | SMS magic-links, OTP, IVR, Voice tokens. |
|
||||||
| Stripe | `lib/payments.js` | REST + webhook (`Stripe-Signature`) | Checkout sessions, Billing Portal, card-on-file, Klarna BNPL. |
|
| Stripe | `lib/payments.js` | REST + webhook (`Stripe-Signature`) | Checkout sessions, Billing Portal, card-on-file, Klarna BNPL. |
|
||||||
| Mailjet | `lib/email.js` (SMTP `in-v3.mailjet.com`) | SMTP | Transactional email (OTP, invoices, magic-links). |
|
| Mailjet | `lib/email.js` (SMTP `in-v3.mailjet.com`) | SMTP | Transactional email (OTP, invoices, magic-links). |
|
||||||
| GenieACS | `lib/devices.js`, `helpers.nbiRequest` | NBI REST / TR-069 | Modem provisioning + diagnostics (legacy CPE fleet). |
|
| GenieACS | `lib/devices.js`, `helpers.nbiRequest` | NBI REST / TR-069 | Modem provisioning + diagnostics for the entire CPE fleet (TR-369/USP via Oktopus was decommissioned May 2026). |
|
||||||
| Oktopus CE | `lib/oktopus.js`, `lib/oktopus-mqtt.js` | REST + MQTT (TR-369/USP) | New CPE fleet. |
|
|
||||||
| Traccar | `lib/traccar.js` | REST + WebSocket | GPS breadcrumbs for field techs. |
|
| Traccar | `lib/traccar.js` | REST + WebSocket | GPS breadcrumbs for field techs. |
|
||||||
| Gemini 2.5 Flash | `lib/vision.js`, `lib/ai.js` | REST (`generativelanguage.googleapis.com`) | Vision OCR + internal AI. |
|
| Gemini 2.5 Flash | `lib/vision.js`, `lib/ai.js` | REST (`generativelanguage.googleapis.com`) | Vision OCR + internal AI. |
|
||||||
| n8n | (via Authentik header proxy) | REST | Workflow automation — long-running batch jobs. |
|
| n8n | (via Authentik header proxy) | REST | Workflow automation — long-running batch jobs. |
|
||||||
|
|
@ -117,22 +115,22 @@ One file per capability; the router in `server.js` dispatches by path prefix.
|
||||||
## 3. Interaction matrix
|
## 3. Interaction matrix
|
||||||
|
|
||||||
Row = caller, column = callee. Cell = primary channel (`REST`, `SSE`, `WS`,
|
Row = caller, column = callee. Cell = primary channel (`REST`, `SSE`, `WS`,
|
||||||
`MQTT`, `SMS`, `SMTP`, `TR-069`, `TR-369`, `Webhook`, `JWT`, `ForwardAuth`,
|
`SMS`, `SMTP`, `TR-069`, `Webhook`, `JWT`, `ForwardAuth`, iCal). Empty =
|
||||||
iCal). Empty = no direct interaction (use the hub as a relay).
|
no direct interaction (use the hub as a relay).
|
||||||
|
|
||||||
| caller ↓ / callee → | ops | client | field | hub | ERPNext | GenieACS | Oktopus | Traccar | Twilio | Stripe | Gemini | Authentik | DocuSeal |
|
| caller ↓ / callee → | ops | client | field | hub | ERPNext | GenieACS | Traccar | Twilio | Stripe | Gemini | Authentik | DocuSeal |
|
||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||
| **ops** | — | — | — | REST + SSE | REST (nginx proxy w/ token) | — (via hub) | — (via hub) | WS (via hub proxy) | — (via hub) | — (via hub) | — (via hub `/vision`) | ForwardAuth | — |
|
| **ops** | — | — | — | REST + SSE | REST (nginx proxy w/ token) | — (via hub) | WS (via hub proxy) | — (via hub) | — (via hub) | — (via hub `/vision`) | ForwardAuth | — |
|
||||||
| **client** | — | — | — | REST + SSE | — (via hub) | — | — | — | — (via hub) | — (via hub `checkout-link`) | — (via hub) | JWT (magic-link, not Authentik) | — |
|
| **client** | — | — | — | REST + SSE | — (via hub) | — | — | — (via hub) | — (via hub `checkout-link`) | — (via hub) | JWT (magic-link, not Authentik) | — |
|
||||||
| **field** | — | — | — | REST | REST (legacy, token) | — | — | — | — | — | — | — | — |
|
| **field** | — | — | — | REST | REST (legacy, token) | — | — | — | — | — | — | — |
|
||||||
| **website** | — | — | — | REST (`/api/checkout`, `/api/order`) | — (via hub) | — | — | — | — | — | — | — | — |
|
| **website** | — | — | — | REST (`/api/checkout`, `/api/order`) | — (via hub) | — | — | — | — | — | — | — |
|
||||||
| **hub** | SSE | SSE | — | — | REST (`erpFetch`, `Authorization: token ...`) | REST (NBI `nbiRequest`) | REST + MQTT | REST + WS | SMS REST + Webhook | REST + Webhook (`Stripe-Signature`) | REST | — | REST + Webhook |
|
| **hub** | SSE | SSE | — | — | REST (`erpFetch`, `Authorization: token ...`) | REST (NBI `nbiRequest`) | REST + WS | SMS REST + Webhook | REST + Webhook (`Stripe-Signature`) | REST | REST (`/auth/users` admin) | REST + Webhook |
|
||||||
| **modem-bridge** | — | — | — | — (hub calls it, `:3301`) | — | — | — | — | — | — | — | — | — |
|
| **modem-bridge** | — | — | — | — (hub calls it, `:3301`) | — | — | — | — | — | — | — | — |
|
||||||
| **ERPNext** | — | — | — | — (fire-and-forget HTTP to hub `/flow/*` from server scripts) | — | — | — | — | — | — | — | — | — |
|
| **ERPNext** | — | — | — | — (fire-and-forget HTTP to hub `/flow/*` from server scripts) | — | — | — | — | — | — | — | — |
|
||||||
| **Traefik** | Forwards | Forwards | Forwards | Forwards | Forwards | — | — | — | — | — | — | ForwardAuth | — |
|
| **Traefik** | Forwards | Forwards | Forwards | Forwards | Forwards | — | — | — | — | — | ForwardAuth | — |
|
||||||
| **Twilio** | — | — | — | Webhook (`/webhook/twilio/*`) | — | — | — | — | — | — | — | — | — |
|
| **Twilio** | — | — | — | Webhook (`/webhook/twilio/*`) | — | — | — | — | — | — | — | — |
|
||||||
| **Stripe** | — | — | — | Webhook (`/webhook/stripe`) | — | — | — | — | — | — | — | — | — |
|
| **Stripe** | — | — | — | Webhook (`/webhook/stripe`) | — | — | — | — | — | — | — | — |
|
||||||
| **DocuSeal**| — | — | — | Webhook (`/contract/*`) | — | — | — | — | — | — | — | — | — |
|
| **DocuSeal**| — | — | — | Webhook (`/contract/*`) | — | — | — | — | — | — | — | — |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -292,7 +290,7 @@ copy; the authoritative write goes to the listed owner.
|
||||||
| Flow Template | ERPNext `Flow Template` (custom) | CRUD in `lib/flow-templates.js` | ops `/agent-flows` |
|
| Flow Template | ERPNext `Flow Template` (custom) | CRUD in `lib/flow-templates.js` | ops `/agent-flows` |
|
||||||
| Flow Run | ERPNext `Flow Run` (custom) | written from `lib/flow-runtime.js` | ops `/agent-flows` |
|
| Flow Run | ERPNext `Flow Run` (custom) | written from `lib/flow-runtime.js` | ops `/agent-flows` |
|
||||||
| **Exceptions** | | | |
|
| **Exceptions** | | | |
|
||||||
| Live modem state (WAN IP, SNR, host list) | GenieACS / Oktopus | hub never persists — always fetched live | — |
|
| Live modem state (WAN IP, SNR, host list) | GenieACS | hub never persists — always fetched live | — |
|
||||||
| GPS breadcrumbs | Traccar | hub proxies WS + REST | ops `/dispatch` map |
|
| GPS breadcrumbs | Traccar | hub proxies WS + REST | ops `/dispatch` map |
|
||||||
| JWT sessions | hub process memory + browser `localStorage` | — | no server-side session store |
|
| JWT sessions | hub process memory + browser `localStorage` | — | no server-side session store |
|
||||||
|
|
||||||
|
|
@ -303,7 +301,6 @@ copy; the authoritative write goes to the listed owner.
|
||||||
| Channel | Transport | Producer | Consumer | Topic / payload |
|
| Channel | Transport | Producer | Consumer | Topic / payload |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Ops / portal live updates | SSE | `lib/sse.js` | `apps/ops`, `apps/client` | `customer:<name>`, `conversations`, `dispatch`, ad-hoc |
|
| Ops / portal live updates | SSE | `lib/sse.js` | `apps/ops`, `apps/client` | `customer:<name>`, `conversations`, `dispatch`, ad-hoc |
|
||||||
| TR-369 device events | MQTT | Oktopus broker | `lib/oktopus-mqtt.js` worker | device-online, heartbeat |
|
|
||||||
| GPS breadcrumbs | WebSocket | Traccar | `lib/traccar.js` proxy → ops dispatch | `deviceId`, `latitude`, `longitude`, `speed` |
|
| GPS breadcrumbs | WebSocket | Traccar | `lib/traccar.js` proxy → ops dispatch | `deviceId`, `latitude`, `longitude`, `speed` |
|
||||||
| Twilio Media Streams | WebSocket upgrade on hub `/voice/ws` | Twilio | `lib/voice-agent.js` | raw audio frames for IVR agent |
|
| Twilio Media Streams | WebSocket upgrade on hub `/voice/ws` | Twilio | `lib/voice-agent.js` | raw audio frames for IVR agent |
|
||||||
| SSE keep-alive | server-pushed `: ping` | `lib/sse.js` 25 s interval | browsers | heartbeat |
|
| SSE keep-alive | server-pushed `: ping` | `lib/sse.js` 25 s interval | browsers | heartbeat |
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,34 @@
|
||||||
|
|
||||||
## 1. Executive Summary & Platform Strategy
|
## 1. Executive Summary & Platform Strategy
|
||||||
|
|
||||||
Gigafibre FSM represents the complete operations platform for Gigafibre, shifting from a polling-based legacy PHP system to a modern, real-time push ecosystem (Vue 3, Node.js, ERPNext, TR-369).
|
Gigafibre FSM is the operations platform for Gigafibre. It replaces a
|
||||||
|
legacy PHP/MariaDB stack with a real-time push ecosystem (Vue 3,
|
||||||
|
Node.js, ERPNext) running on a single Proxmox VM at `96.125.196.67`.
|
||||||
|
|
||||||
The strategy pivots around a **unified core platform** running entirely on a remote Proxmox VM (96.125.196.67):
|
Core pillars:
|
||||||
- **ERPNext v16** as the undisputed Source of Truth (CRM, billing, ticketing).
|
- **ERPNext v16** — undisputed Source of Truth (CRM, billing, ticketing).
|
||||||
- **Targo Ops PWA** as the single pane of glass for internal teams.
|
- **Ops SPA** at `erp.gigafibre.ca/ops/` — single pane of glass for
|
||||||
- **Targo Hub** as the real-time API gateway (SMS, SSE, AI, TR-069 proxy).
|
internal teams (dispatch, clients, settings, agent flows).
|
||||||
- **Client.gigafibre.ca** for customer self-service.
|
- **targo-hub** at `msg.gigafibre.ca` — real-time API gateway (SMS,
|
||||||
|
SSE, AI, OAuth admin, Stripe webhooks, Traccar proxy).
|
||||||
|
- **Client portal** at `client.gigafibre.ca` — customer self-service.
|
||||||
|
|
||||||
**Legacy Retirement Plan (April-May 2026):**
|
**Decommissioned (May 2026):**
|
||||||
- *Retire* `dispatch-app` — Functionality now in Ops + lightweight mobile tech page (`/t/{token}`).
|
- ✗ `Oktopus CE` (TR-369 stack at `oss.gigafibre.ca`) — broker spammed
|
||||||
- *Retire* `apps/field` — Redundant to the mobile tech page workflow.
|
75 GB of debug logs over 13 days, took ERPNext down for 4. Stack
|
||||||
- *Retire* `auth.targo.ca` — Fully migrated to `id.gigafibre.ca` Authentik.
|
removed (containers + volumes + images). The hub gates the integration
|
||||||
|
behind `OKTOPUS_DISABLED=1` so the modules can be re-enabled later if
|
||||||
|
we deploy a different USP controller.
|
||||||
|
- ✗ `dispatch-app` (legacy PHP SPA at `dispatch.gigafibre.ca`) — now
|
||||||
|
301-redirects to `/ops/#/dispatch`. nginx config at
|
||||||
|
`/opt/dispatch-app/nginx.conf` on the prod box.
|
||||||
|
- ✗ `apps/field` — replaced by the lightweight mobile tech page at
|
||||||
|
`/t/{token}` (server-rendered by `services/targo-hub/lib/tech-mobile.js`).
|
||||||
|
|
||||||
|
**Two Authentik instances, in parallel — not a migration:**
|
||||||
|
- `auth.targo.ca` (staff) — protects /ops/, n8n, Gitea; OAuth provider
|
||||||
|
for ERPNext sign-in.
|
||||||
|
- `id.gigafibre.ca` (clients) — protects the customer portal.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -30,17 +46,19 @@ Internet
|
||||||
│
|
│
|
||||||
├─ Traefik v2.11 (:80/:443, Let's Encrypt, ForwardAuth)
|
├─ Traefik v2.11 (:80/:443, Let's Encrypt, ForwardAuth)
|
||||||
│
|
│
|
||||||
├─ Authentik SSO (id.gigafibre.ca) → Secures /ops/ and client portal
|
├─ Authentik (auth.targo.ca) → SSO for staff (ops, n8n, Gitea, ERPNext OAuth)
|
||||||
|
├─ Authentik (id.gigafibre.ca) → SSO for client portal
|
||||||
│
|
│
|
||||||
├─ ERPNext v16.10.1 (erp.gigafibre.ca) → 9 containers (db, redis, workers)
|
├─ ERPNext v16.10.1 (erp.gigafibre.ca) → 9 containers (db, redis, backend, queues, scheduler, websocket, n8n, n8n-proxy)
|
||||||
│
|
│
|
||||||
├─ Targo Ops App (erp.gigafibre.ca/ops/) → Served via nginx:alpine
|
├─ Ops SPA (erp.gigafibre.ca/ops/) → Served via nginx:alpine from /opt/ops-app/
|
||||||
|
├─ Dispatch redirect (dispatch.gigafibre.ca) → 301 → /ops/#/dispatch (former dispatch-app, decommissioned)
|
||||||
│
|
│
|
||||||
├─ n8n (n8n.gigafibre.ca) → Auto-login proxy wired to Authentik headers
|
├─ targo-hub (msg.gigafibre.ca) → Node 20, /opt/targo-hub/
|
||||||
|
├─ DocuSeal (docs.gigafibre.ca) → Contract e-signature
|
||||||
|
├─ traccar-proxy → nginx relay for Traccar UI
|
||||||
│
|
│
|
||||||
├─ Oktopus CE (oss.gigafibre.ca) → TR-369 CPE management
|
└─ Marketing site (www.gigafibre.ca) → React/Vite/Tailwind
|
||||||
│
|
|
||||||
└─ WWW / Frontend (www.gigafibre.ca) → React marketing site
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**DNS Configuration (Cloudflare):**
|
**DNS Configuration (Cloudflare):**
|
||||||
|
|
@ -84,13 +102,49 @@ Internet
|
||||||
## 4. Security & Authentication Flow
|
## 4. Security & Authentication Flow
|
||||||
|
|
||||||
```text
|
```text
|
||||||
User → app.gigafibre.ca
|
Staff user → erp.gigafibre.ca/ops/ (or n8n, Gitea)
|
||||||
→ Traefik checks session via ForwardAuth middleware
|
→ Traefik checks session via ForwardAuth middleware
|
||||||
→ Flow routed to Authentik (id.gigafibre.ca)
|
→ Outpost validates with Authentik staff (auth.targo.ca)
|
||||||
→ Authorized? Request forwarded to native container with 'X-Authentik-Email' header
|
→ Authorized? Request forwarded to upstream container
|
||||||
|
with X-Authentik-Email + X-Authentik-Groups headers
|
||||||
|
→ Ops SPA reads X-Authentik-Email; useUserGroups maps groups
|
||||||
|
to in-app capabilities
|
||||||
|
|
||||||
|
Customer user → client.gigafibre.ca
|
||||||
|
→ Traefik checks session via separate ForwardAuth chain
|
||||||
|
→ Outpost validates with Authentik client (id.gigafibre.ca)
|
||||||
```
|
```
|
||||||
- **ForwardAuth (`authentik-client@file`):** Currently protects `erp.gigafibre.ca/ops/`, `n8n`, and `hub`.
|
|
||||||
- **API Security:** Frontends use the Authentik session proxy; Backend services/scripts use the `Authorization: token` headers directly hitting Frappe's `/api/method`.
|
**Two distinct ForwardAuth middlewares**:
|
||||||
|
- `authentik@file` → backed by `auth.targo.ca` (staff)
|
||||||
|
- `authentik-client@file` → backed by `id.gigafibre.ca` (customers)
|
||||||
|
|
||||||
|
**ERPNext OAuth** — `auth.targo.ca` is also configured as a Frappe
|
||||||
|
Social Login Key (provider name `Authentik`). The login page at
|
||||||
|
`/login` shows both the password form and the "Login with Authentik"
|
||||||
|
button. OAuth client_id `P0rFFdq2hhun7hOLwkF5zm87vvDqcVYAhLtoZnFX`,
|
||||||
|
redirect_uri `/api/method/frappe.integrations.oauth2_logins.custom/authentik`.
|
||||||
|
|
||||||
|
**Adding new users** is centralized through the hub, not the Authentik
|
||||||
|
admin UI. The ops Settings page (`Settings → Utilisateurs → Inviter`)
|
||||||
|
hits `POST /auth/users` on `msg.gigafibre.ca` which:
|
||||||
|
1. Creates the Authentik user (random username from local-part of email,
|
||||||
|
password set explicitly), assigns OPS_GROUPS.
|
||||||
|
2. Sets a temp password (readable, no look-alikes) and emails it via
|
||||||
|
the hub's Mailjet SMTP — Authentik's own recovery flow isn't wired
|
||||||
|
(`flow_recovery=None` on the brand) and its global SMTP is unset,
|
||||||
|
so the hub does it directly.
|
||||||
|
3. Creates the matching ERPNext User (System User, social_logins =
|
||||||
|
[{provider:authentik, userid:email}]) so OAuth finds it on first
|
||||||
|
login.
|
||||||
|
|
||||||
|
The temp password is also returned to the admin (UI shows it with a
|
||||||
|
copy button) so they can hand it over manually if Mailjet drops the
|
||||||
|
message. See `services/targo-hub/lib/auth.js` for the full flow.
|
||||||
|
|
||||||
|
**API Security**: frontends rely on the Authentik session cookie
|
||||||
|
forwarded by Traefik. Backend scripts and the hub use
|
||||||
|
`Authorization: token <ERP_SERVICE_TOKEN>` Bearer headers.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -111,7 +165,11 @@ When a CSR clicks "Diagnostiquer" in the Ops app:
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Development Gotchas
|
## 6. Development Gotchas
|
||||||
|
|
||||||
1. **Traefik v3** is incompatible with Docker 29 due to API changes. Stay on v2.11.
|
1. **Traefik v3** is incompatible with Docker 29 due to API changes. Stay on v2.11.
|
||||||
2. **MongoDB 5+** (Oktopus) requires AVX extensions. Proxmox CPU must be set to `host`.
|
2. **Never click "Generate Keys"** for the Administrator user in ERPNext — it breaks the `targo-hub` API token (silently).
|
||||||
3. Never click "Generate Keys" for the Administrator user in ERPNext or it breaks the `targo-hub` API token.
|
3. **Traccar API** supports only one `deviceId` per request. Use parallel polling (`Promise.allSettled`) — see `services/targo-hub/lib/traccar.js`.
|
||||||
4. **Traccar API** supports only one `deviceId` per request. Use parallel polling.
|
4. **Docker log rotation** is set globally via `/etc/docker/daemon.json` (`max-size=100m, max-file=3`). Applied at container creation — old containers keep their previous (uncapped) policy until you `compose up -d --force-recreate` them. We learned this the hard way when the Oktopus broker filled `/var/sdb` with 75 GB of debug logs in 13 days.
|
||||||
|
5. **Weekly prune** runs via `/etc/cron.d/docker-prune` Sunday 03:00 ET — clears anything not used in 30 days. Don't add a stack you only run monthly without `restart: always` or it'll get pruned out.
|
||||||
|
6. **PostgreSQL transaction-aborted errors** in the backend log — usually benign (one bad query in the Frappe scheduler) but if persistent, it's the connection pool needing a recycle. `docker restart erpnext-backend-1` resolves.
|
||||||
|
7. **Authentik recovery flow** isn't configured on the brand. Don't use `recovery_email/` from the API — use the hub invite flow described in §4 instead.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ modes. Open the one that matches the feature you're changing.
|
||||||
| [tech-mobile.md](tech-mobile.md) | Field tech app (three surfaces: SSR `/t/{jwt}`, transitional `apps/field/`, unified `/ops/#/j/*`). Native camera → Gemini scanner, equipment install/remove, JWT auth, offline queue |
|
| [tech-mobile.md](tech-mobile.md) | Field tech app (three surfaces: SSR `/t/{jwt}`, transitional `apps/field/`, unified `/ops/#/j/*`). Native camera → Gemini scanner, equipment install/remove, JWT auth, offline queue |
|
||||||
| [customer-portal.md](customer-portal.md) | Passwordless customer self-service at `portal.gigafibre.ca`: magic-link email (24h JWT), invoice + ticket view, Stripe-linked payment flows |
|
| [customer-portal.md](customer-portal.md) | Passwordless customer self-service at `portal.gigafibre.ca`: magic-link email (24h JWT), invoice + ticket view, Stripe-linked payment flows |
|
||||||
| [billing-payments.md](billing-payments.md) | Stripe integration (Checkout, Billing Portal, webhook), subscription lifecycle, invoice generation, payment reconciliation, PPA (Plan de paiement automatique), Klarna BNPL |
|
| [billing-payments.md](billing-payments.md) | Stripe integration (Checkout, Billing Portal, webhook), subscription lifecycle, invoice generation, payment reconciliation, PPA (Plan de paiement automatique), Klarna BNPL |
|
||||||
| [cpe-management.md](cpe-management.md) | CPE fleet: GenieACS (TR-069), Oktopus (TR-369), provisioning, diagnostics, TP-Link XX230v / Deco deep probe via modem-bridge, migration plan |
|
| [cpe-management.md](cpe-management.md) | CPE fleet: GenieACS (TR-069) provisioning + diagnostics, TP-Link XX230v / Deco deep probe via modem-bridge, 3-way diagnostic-swap workflow |
|
||||||
| [vision-ocr.md](vision-ocr.md) | Camera-based scanning via Gemini 2.5 Flash — barcode, equipment label, invoice OCR. Hub endpoints `/vision/*`, `useScanner` composable, offline queue, AI_API_KEY rotation policy, ERPNext relationships triggered by a scan |
|
| [vision-ocr.md](vision-ocr.md) | Camera-based scanning via Gemini 2.5 Flash — barcode, equipment label, invoice OCR. Hub endpoints `/vision/*`, `useScanner` composable, offline queue, AI_API_KEY rotation policy, ERPNext relationships triggered by a scan |
|
||||||
| [flow-editor.md](flow-editor.md) | Agent-flows module: Flow Template + Flow Run doctypes, trigger catalogue, step types, runtime contract, UI editor at `/ops/#/agent-flows` |
|
| [flow-editor.md](flow-editor.md) | Agent-flows module: Flow Template + Flow Run doctypes, trigger catalogue, step types, runtime contract, UI editor at `/ops/#/agent-flows` |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,124 @@
|
||||||
# Gigafibre FSM — CPE Hardware Management
|
# Gigafibre FSM — CPE Hardware Management
|
||||||
|
|
||||||
> A consolidated guide for managing Customer Premises Equipment (CPE) fleets. This document covers TR-069/TR-369 protocols, ACS migration strategies, and deep hardware diagnostics (specifically the TP-Link XX230v / Deco).
|
> Managing the customer-premises equipment fleet (ONTs, routers, mesh
|
||||||
|
> nodes). Covers TR-069 (GenieACS), the modem-bridge for deep TP-Link
|
||||||
|
> diagnostics, and the diagnostic-swap workflow.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Protocol Strategy (TR-069 to TR-369)
|
## 1. Protocol & Tooling Stack
|
||||||
|
|
||||||
We are gradually migrating our management plane from **GenieACS** (TR-069, HTTP/SOAP polling) to **Oktopus CE** (TR-369, real-time USP over MQTT/WebSocket).
|
The current ACS is **GenieACS** (TR-069 / CWMP — HTTP/SOAP polling).
|
||||||
|
It's external to the prod box (separate VM managed by the network
|
||||||
|
team). The hub talks to it via the GenieACS NBI (Northbound Interface)
|
||||||
|
on the internal network.
|
||||||
|
|
||||||
**The goal is bidirectional, real-time device management.** TR-069 waits for the next "inform" interval (often hours) before executing a reboot or reading parameters. TR-369 maintains a constant socket connection.
|
**About TR-369 / USP** — we ran a parallel Oktopus CE deployment for
|
||||||
|
USP (TR-369 over MQTT/WebSocket) but **decommissioned it in May 2026**
|
||||||
|
after the broker filled the disk with debug spam. The integration
|
||||||
|
modules in the hub (`lib/oktopus.js`, `lib/oktopus-mqtt.js`) remain
|
||||||
|
in the tree gated behind `OKTOPUS_DISABLED=1` so we can re-enable them
|
||||||
|
later if we settle on a different USP controller. For now, all CPE
|
||||||
|
management goes through TR-069 + GenieACS.
|
||||||
|
|
||||||
### Migration Phases
|
**Polling cadence** — GenieACS receives an Inform from each ONT every
|
||||||
1. **Parallel Run:** Oktopus is deployed at `oss.gigafibre.ca`. It has a TR-069 adapter, allowing it to natively accept legacy CWMP connections.
|
~5 minutes (configurable in the device firmware). For real-time deep
|
||||||
2. **Translation:** We must manually map old GenieACS JS provision scripts directly against the Oktopus event subscriptions and policy webhook engine.
|
dives that can't wait for the next Inform window, the hub falls back
|
||||||
3. **Migration:** Update the `Device.ManagementServer.URL` on CPEs to point to the new Oktopus TR-069 Adapter. Keep GenieACS read-only.
|
to:
|
||||||
4. **Upgrade to TR-369:** Where CPE firmware allows (e.g., ZTE F680, Nokia G-010G-Q 3.x+), push firmware updates that include a native USP agent and point them to Oktopus MQTT.
|
- **modem-bridge** (`services/modem-bridge/`) — Playwright-driven HTTP
|
||||||
|
client that scrapes encrypted TR-181 parameters from the modem's
|
||||||
|
native admin UI. Specifically targets the TP-Link XX230v / Deco mesh.
|
||||||
|
- **OLT SNMP** — direct SNMPv2 walk against the OLT for ONT optical
|
||||||
|
status, when even the modem itself is unreachable.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. TP-Link XX230v (Deco XE75) Deep Diagnostics
|
## 2. TP-Link XX230v (Deco XE75) Deep Diagnostics
|
||||||
|
|
||||||
The TP-Link XX230v supports a rich TR-181 data model. When customers report "WiFi issues", CSRs and Techs should not blindly swap the hardware. The following endpoints must be polled to ascertain the actual root cause of the fault.
|
The XX230v exposes a rich TR-181 data model. When customers report
|
||||||
|
"WiFi issues", CSRs and techs should not blindly swap the hardware.
|
||||||
|
Poll these endpoints first to find the actual root cause.
|
||||||
|
|
||||||
### A. Optical Signal (Is it the Fibre?)
|
### A. Optical Signal (Is it the Fibre?)
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Device.Optical.Interface.1.Stats.SignalRxPower → target: -8 to -25 dBm
|
Device.Optical.Interface.1.Stats.SignalRxPower → target: -8 to -25 dBm
|
||||||
Device.Optical.Interface.1.Stats.ErrorsSent
|
Device.Optical.Interface.1.Stats.ErrorsSent
|
||||||
```
|
```
|
||||||
*Diagnosis:* If RxPower < -25 dBm, there is a dirty connector or a fibre break. It is **not** a hardware fault with the ONT. Do not swap it.
|
|
||||||
|
**Diagnosis** — RxPower < -25 dBm = dirty connector or fibre break.
|
||||||
|
**Not** an ONT hardware fault. Don't swap; dispatch a fibre tech.
|
||||||
|
|
||||||
### B. WiFi Radio & Topology (Is it Interference?)
|
### B. WiFi Radio & Topology (Is it Interference?)
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Device.WiFi.Radio.1.Stats.Noise → Interference measure (2.4Ghz)
|
Device.WiFi.Radio.1.Stats.Noise → 2.4 GHz interference
|
||||||
Device.WiFi.Radio.2.Stats.Noise → Interference measure (5GHz)
|
Device.WiFi.Radio.2.Stats.Noise → 5 GHz interference
|
||||||
Device.WiFi.MultiAP.APDevice.{i}.Radio.{j}.Utilization → Backhaul traffic load on Deco Mesh
|
Device.WiFi.MultiAP.APDevice.{i}.Radio.{j}.Utilization → mesh backhaul load
|
||||||
```
|
```
|
||||||
*Diagnosis:* High noise/errors on a specific band indicates environmental channel congestion. If the backhaul utilization is extremely high on the satellite Deco, tell the customer to move it closer to the main unit.
|
|
||||||
|
**Diagnosis** — high noise/errors on a band = environmental channel
|
||||||
|
congestion (neighbours, microwave, baby monitor). High backhaul
|
||||||
|
utilization on a satellite Deco = customer needs to move it closer
|
||||||
|
to the main unit.
|
||||||
|
|
||||||
### C. Live Speed Test (Is it the Client Device?)
|
### C. Live Speed Test (Is it the Client Device?)
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Device.IP.Diagnostics.DownloadDiagnostics.DiagnosticsState = "Requested"
|
Device.IP.Diagnostics.DownloadDiagnostics.DiagnosticsState = "Requested"
|
||||||
```
|
```
|
||||||
*Diagnosis:* You can mandate the ONT line to perform its own speed test, eliminating WiFi latency variables. If the ONT download test is fast, but the customer's iPhone is slow, the iPhone or the WiFi signal is to blame.
|
|
||||||
|
**Diagnosis** — kicks off a server-to-ONT speed test, which eliminates
|
||||||
|
WiFi-side latency variables. ONT speed test fast + customer's iPhone
|
||||||
|
slow → the iPhone or the WiFi link is the bottleneck, not the line.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. The "Diagnostic Swap" Workflow
|
## 3. The "Diagnostic Swap" Workflow
|
||||||
|
|
||||||
A common gap occurs when techs swap equipment simply because they aren't sure what is defective. This creates inventory chaos.
|
A common gap occurs when techs swap equipment simply because they
|
||||||
|
aren't sure what is defective. This creates inventory chaos.
|
||||||
|
|
||||||
**We are pivoting from binary status (`Défectueux`) to a 3-way diagnostic status:**
|
We use a **3-way diagnostic status** instead of a binary `Défectueux`:
|
||||||
|
|
||||||
1. **Remplacement Définitif** — The equipment is dead.
|
1. **Remplacement définitif** — the equipment is dead.
|
||||||
*(Old = Défectueux, New = Actif)*
|
*(Old → Défectueux, New → Actif)*
|
||||||
2. **Swap Diagnostic** — Swapping to test if the problem resolves.
|
2. **Swap diagnostic** — swapping to test if the problem resolves.
|
||||||
*(Old = En diagnostic, New = Actif temporary)*
|
*(Old → En diagnostic, New → Actif (temporary))*
|
||||||
3. **Retour de diagnostic** — The old unit was fine. It is returned.
|
3. **Retour de diagnostic** — the old unit was actually fine.
|
||||||
*(Old = Actif (put back into use), Test unit = Retourné)*
|
*(Old → Actif (returned to use), Test unit → Retourné)*
|
||||||
|
|
||||||
If a tech chooses **Swap diagnostic**, an ERPNext Task is automatically generated scheduling a follow-through test on the removed hardware within 7 days. If the unit tests fine at the warehouse, it is restored back into `En inventaire` instead of being trashed.
|
If a tech chooses **Swap diagnostic**, an ERPNext Task is automatically
|
||||||
|
generated scheduling a follow-through test on the removed hardware
|
||||||
|
within 7 days. If the unit tests fine at the warehouse, it goes back
|
||||||
|
to `En inventaire` instead of being trashed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Hub Endpoints (`/devices/*`)
|
||||||
|
|
||||||
|
The ops "Diagnostiquer" button on a customer or equipment row hits the
|
||||||
|
hub, which orchestrates GenieACS / OLT / modem-bridge in the right
|
||||||
|
order:
|
||||||
|
|
||||||
|
1. `/devices/lookup?serial=X` — finds the ONT by serial in GenieACS.
|
||||||
|
2. The hub returns the latest Inform snapshot (interface, mesh, wifi,
|
||||||
|
opticalStatus). If older than 60s, it kicks an immediate
|
||||||
|
`Refresh` task to GenieACS.
|
||||||
|
3. For TP-Link models specifically, deeper params (encrypted TR-181)
|
||||||
|
are fetched via modem-bridge if needed.
|
||||||
|
4. Final response is a consolidated JSON consumed by
|
||||||
|
`apps/ops/src/components/equipment/EquipmentDiagnostic.vue`.
|
||||||
|
|
||||||
|
See [module-interactions.md](../architecture/module-interactions.md)
|
||||||
|
for the full call graph.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Cross-references
|
||||||
|
|
||||||
|
- [../architecture/module-interactions.md](../architecture/module-interactions.md) — hub call graph for device flows.
|
||||||
|
- [vision-ocr.md](vision-ocr.md) — barcode scan during install (ONT
|
||||||
|
serial, MAC) feeds back into Service Equipment.
|
||||||
|
- [dispatch.md](dispatch.md) §5.6 — equipment install/swap from the
|
||||||
|
tech mobile page.
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,59 @@ All French strings below are pulled directly from `DispatchPage.vue`.
|
||||||
|
|
||||||
### Top bar
|
### Top bar
|
||||||
|
|
||||||
"Ressources" (resource filter), "Filtres" (tag filter), "Aujourd'hui" (jump-to-today), "Jour / Semaine / Mois" view toggle, "Planning" mode for availability editing, "Carte" for the inline map, "Publier" for the publish modal, "+ WO" for the work-order creator, "ERP" status pill.
|
The header is split in three flex regions (left / center / right), all
|
||||||
|
icons are single-color Lucide-style strokes pulled from `ICON.*` in
|
||||||
|
`useHelpers.js` — no emojis or multi-color glyphs.
|
||||||
|
|
||||||
|
**Left region** (filters + view selector):
|
||||||
|
- **Search** — type a tech name or saved-preset chip to scope the grid.
|
||||||
|
- **Resource type chip** `[👥 N ▾]` — single dropdown anchored to the
|
||||||
|
current selection. Default is `human` (techs only). Materials are
|
||||||
|
secondary; the `Matériel` and `Tous` entries appear only if there's
|
||||||
|
at least one material resource. Persisted in localStorage.
|
||||||
|
- **Board view dropdown** `[Vue principale ▾]` — replaces the inline
|
||||||
|
tabs. Click → list of saved board views + a future "+ Nouvelle vue"
|
||||||
|
entry.
|
||||||
|
- **Filters/settings** (sliders icon) — opens the filter panel
|
||||||
|
(status / group / tags / hide-absent). The icon is `sliders`, *not*
|
||||||
|
`wrench` — wrench is reserved for the materials filter so the two
|
||||||
|
concerns don't share a glyph.
|
||||||
|
- **Projets** — visible only when there are team-jobs (`assistants[]`
|
||||||
|
populated); shows the project count badge.
|
||||||
|
|
||||||
|
**Center region** (period + view + planning):
|
||||||
|
- **Aujourd'hui** jump-to-today, `‹ ›` prev/next period.
|
||||||
|
- **Jour / Semaine / Mois** view toggle.
|
||||||
|
- **Planning toggle** (calendar icon, label `Planning`) — switches the
|
||||||
|
grid to availability/shift editing mode.
|
||||||
|
|
||||||
|
**Right region** (signal + CTAs + overflow):
|
||||||
|
- **Surchargé** alert (triangle icon) — appears only when at least
|
||||||
|
one tech is over capacity for the selected day; tooltip lists the
|
||||||
|
techs and their %.
|
||||||
|
- **Jobs non assignées** (clipboard icon) with a badge for the
|
||||||
|
unscheduled count.
|
||||||
|
- **Carte** (map icon) — toggles the inline Mapbox panel (Day view
|
||||||
|
only). The map defaults to centered on Gigafibre HQ
|
||||||
|
`(lng=-73.6756, lat=45.1599, zoom=10)` covering Sainte-Clotilde +
|
||||||
|
Châteauguay + Napierville + Hemmingford. Clicking a tech in the
|
||||||
|
resource list flies the map to their position (live Traccar fix
|
||||||
|
if online, else saved home base).
|
||||||
|
- **Publier** (purple CTA) with a draft-count badge.
|
||||||
|
- **+ WO** (indigo CTA) — opens `WoCreateModal`.
|
||||||
|
- **⋯ overflow menu** — hosts the secondary actions: Actualiser,
|
||||||
|
Assistant IA (collapses the NLP input bar by default), Offres aux
|
||||||
|
techs (with green count badge), Ressources & GPS (opens the
|
||||||
|
Traccar / tech management modal — see §4.x), and "Ouvrir ERPNext"
|
||||||
|
(with inline status dot for the API connection).
|
||||||
|
|
||||||
|
Two clarifications worth knowing:
|
||||||
|
- The `.sb-header` container uses `overflow:visible` so dropdowns can
|
||||||
|
spill below it. Earlier `overflow:hidden` clipped the ⋯ menu — fixed
|
||||||
|
in commit `16343b6`.
|
||||||
|
- All dropdowns close on Escape, on click outside, and after picking
|
||||||
|
an item. The handler chain is shared with the existing `ctxMenu` /
|
||||||
|
`techCtx` / `assistCtx` close logic.
|
||||||
|
|
||||||
### Timeline view (`TimelineRow.vue`)
|
### Timeline view (`TimelineRow.vue`)
|
||||||
|
|
||||||
|
|
@ -175,7 +227,45 @@ Pricing is composed from `PRICING_PRESETS` (in `useJobOffers`) as `displacement$
|
||||||
|
|
||||||
### Context menu (`SbContextMenu.vue`)
|
### Context menu (`SbContextMenu.vue`)
|
||||||
|
|
||||||
Right-click on a tech row or job block opens a context menu wired through `useContextMenus` — quick reassign, cancel, open in ERP, copy magic link, copy iCal URL.
|
Right-click on a tech row, a tech pin on the map, or a job block opens
|
||||||
|
a context menu wired through `useContextMenus` — quick reassign,
|
||||||
|
cancel, open in ERP, copy magic link, copy iCal URL.
|
||||||
|
|
||||||
|
Tech-specific menu (`techCtx`) entries:
|
||||||
|
- 🗺 Voir sur la carte
|
||||||
|
- 🔀 Optimiser la route
|
||||||
|
- 🏷 Skills / Tags
|
||||||
|
- 📅 Copier le lien iCal
|
||||||
|
- 📍 Adresse de départ… — opens the home-base dialog (see below)
|
||||||
|
- 🎯 Choisir sur la carte — enters the geoFixTech pick mode
|
||||||
|
- ↗ Ouvrir dans ERPNext
|
||||||
|
|
||||||
|
### Tech home base (departure point)
|
||||||
|
|
||||||
|
Each tech has a `latitude` / `longitude` on `Dispatch Technician` that
|
||||||
|
serves as their start-of-day coordinate when no live Traccar fix is
|
||||||
|
available. The dispatch route optimizer uses these as the origin
|
||||||
|
when computing the optimal job sequence.
|
||||||
|
|
||||||
|
**Editing it** — three paths converge on `saveTechHome(tech, lng, lat)`
|
||||||
|
in `useTechManagement.js`:
|
||||||
|
|
||||||
|
1. **📍 button next to a tech in the GPS sidebar** — opens a 2-option
|
||||||
|
chooser: "Saisir une adresse" (free-text geocoded via OpenStreetMap
|
||||||
|
Nominatim) or "Cliquer sur la carte" (entry into geoFixTech mode).
|
||||||
|
2. **Right-click on the tech's pin on the map** — same context menu
|
||||||
|
entries as above.
|
||||||
|
3. **Quick paste** — the address dialog also accepts a literal
|
||||||
|
`lat, lng` pair (e.g. `45.16, -73.68`) for power users.
|
||||||
|
|
||||||
|
Pick mode (`geoFixTech`) shows an indigo banner at the top of the
|
||||||
|
screen with the tech's name and an Annuler button. Cursor is set to
|
||||||
|
crosshair on the map. Next click captures lng/lat → PUTs to ERPNext →
|
||||||
|
recomputes routes. ESC cancels.
|
||||||
|
|
||||||
|
**Default fallback** for techs without saved coords is Gigafibre HQ —
|
||||||
|
1867 chemin de la Rivière, Sainte-Clotilde QC
|
||||||
|
(`lng=-73.6756177, lat=45.1599145`). Set in `apps/ops/src/stores/dispatch.js`.
|
||||||
|
|
||||||
### Drag-drop rules (enforced in `useDragDrop`)
|
### Drag-drop rules (enforced in `useDragDrop`)
|
||||||
|
|
||||||
|
|
@ -318,14 +408,29 @@ The Ops PWA never opens this page — it lives alongside Dispatch to replace the
|
||||||
| Ghost occurrences missing | RRULE parse failed client-side — malformed `recurrence_rule` | `apps/ops/src/stores/dispatch.js` `_mapJob` |
|
| Ghost occurrences missing | RRULE parse failed client-side — malformed `recurrence_rule` | `apps/ops/src/stores/dispatch.js` `_mapJob` |
|
||||||
| Tech mobile page shows "not found" | JWT expired (default 72h) — operator must re-publish to regenerate | `services/targo-hub/lib/magic-link.js` |
|
| Tech mobile page shows "not found" | JWT expired (default 72h) — operator must re-publish to regenerate | `services/targo-hub/lib/magic-link.js` |
|
||||||
|
|
||||||
## 9. Retirement Note
|
## 9. Retirement Status (May 2026)
|
||||||
|
|
||||||
The legacy `dispatch-app` (PHP) and `apps/field` (mobile technician SPA) are slated for retirement in April-May 2026, per [../architecture/overview.md](../architecture/overview.md) §1. The replacement pairing is:
|
Both legacy frontends are now decommissioned:
|
||||||
|
|
||||||
- **Ops PWA Dispatch module** — this document — for all scheduling, ranking, publishing.
|
- **`dispatch-app` (PHP)** at `dispatch.gigafibre.ca` —
|
||||||
- **Lightweight tech mobile page** at `/t/{token}` (see §6) for the field-side experience.
|
the nginx serving `/opt/dispatch-app/` was repointed to a single
|
||||||
|
`return 301 https://erp.gigafibre.ca/ops/#/dispatch` rule. Anyone
|
||||||
|
hitting an old bookmarked URL bounces through the redirect into
|
||||||
|
the ops dispatch module without re-authentication noise (the
|
||||||
|
`authentik@file` middleware was removed from the dispatch router so
|
||||||
|
the redirect fires immediately).
|
||||||
|
- **`apps/field`** — the lightweight mobile tech page at `/t/{token}`
|
||||||
|
is the replacement. See §6 of this doc.
|
||||||
|
|
||||||
Do not add new features to `dispatch-app` or `apps/field`. Any feature request should land in `apps/ops/src/modules/dispatch/` or the tech-mobile handler in `services/targo-hub/lib/tech-mobile.js`.
|
The replacement pairing is:
|
||||||
|
|
||||||
|
- **Ops SPA Dispatch module** — this document — for all scheduling,
|
||||||
|
ranking, publishing.
|
||||||
|
- **Tech mobile page** at `/t/{token}` for the field-side experience.
|
||||||
|
|
||||||
|
Do not add new features to `dispatch-app` or `apps/field`. Any feature
|
||||||
|
request should land in `apps/ops/src/modules/dispatch/` or the
|
||||||
|
tech-mobile handler in `services/targo-hub/lib/tech-mobile.js`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,11 @@ SSO for staff surfaces, Stripe Checkout for customer ones).
|
||||||
| Service | URL | Stack |
|
| Service | URL | Stack |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| ERPNext | [erp.gigafibre.ca](https://erp.gigafibre.ca) | Frappe v16 / PostgreSQL |
|
| ERPNext | [erp.gigafibre.ca](https://erp.gigafibre.ca) | Frappe v16 / PostgreSQL |
|
||||||
| Authentik SSO (staff) | [auth.targo.ca](https://auth.targo.ca) | migrating → id.gigafibre.ca |
|
| Authentik SSO (staff) | [auth.targo.ca](https://auth.targo.ca) | OAuth provider for ERPNext + ForwardAuth for ops/n8n/Gitea |
|
||||||
| Authentik SSO (clients) | [id.gigafibre.ca](https://id.gigafibre.ca) | Federated from auth.targo.ca |
|
| Authentik SSO (clients) | [id.gigafibre.ca](https://id.gigafibre.ca) | Customer portal — separate instance, not federated |
|
||||||
| DocuSeal | [sign.gigafibre.ca](https://sign.gigafibre.ca) | Contract signing |
|
| DocuSeal | [sign.gigafibre.ca](https://sign.gigafibre.ca) | Contract signing |
|
||||||
| n8n | [n8n.gigafibre.ca](https://n8n.gigafibre.ca) | Workflow automation |
|
| n8n | [n8n.gigafibre.ca](https://n8n.gigafibre.ca) | Workflow automation |
|
||||||
| Oktopus | [oss.gigafibre.ca](https://oss.gigafibre.ca) | TR-369 CPE controller |
|
| GenieACS | internal `10.5.2.115:7557` | TR-069 NBI — sole CPE controller (Oktopus decommissioned May 2026) |
|
||||||
| GenieACS | internal `10.5.2.115:7557` | TR-069 NBI |
|
|
||||||
| Traccar | [tracker.targointernet.com](https://tracker.targointernet.com) | GPS tracking |
|
| Traccar | [tracker.targointernet.com](https://tracker.targointernet.com) | GPS tracking |
|
||||||
| Website | [www.gigafibre.ca](https://www.gigafibre.ca) | Marketing + address lookup |
|
| Website | [www.gigafibre.ca](https://www.gigafibre.ca) | Marketing + address lookup |
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user