Ecosystem map · current state

Valeron Ecosystem

How the properties connect, where contracts live, and which protocols cross each boundary. Reflects current state as of 2026-04-28: Feature #7 (Gateway Extraction) Phase 6 Track A landed 2026-04-21, the ace_supervisor repo split into robot-supervisor-service + robot-gateway-service, and shared schemas live as @valeron/gateway-contracts (a workspace package inside robot-gateway-service, not its own repo).

Generated 2026-04-28 · Ground truth: docs-platform/architecture/ECOSYSTEM.md + docs-platform/apis/ + docs-platform/architecture/FLOWS.md

Supersedes 2026-04-19-ecosystem-view.html. Deltas vs 04-19: Nav-Jetson hostname re-IPed post-2026-04-22 reflash (v0-ace00v0-ace00-robot); Robot Jetson subgraph relabeled Nav Jetson for clarity (both Jetsons sit on the robot); shared-gateway-protocols collapsed into the robot-gateway-service/packages/contracts/ workspace package @valeron/gateway-contracts v0.2.0; remaining Sequencer / Janice references normalised to Supervisor / robot-supervisor-service (Janice is the persona, not the canonical service name); systemd unit names refreshed per ADR-022 Amendment 2026-04-22 (robot_supervisor_service.service, mobile_field_app.service, deploy paths under /opt/valeron/).

Ecosystem map

Containers = where the code runs. Boxes = repos. Arrows = runtime traffic with protocol labels.

scroll to zoom · drag to pan
--- config: layout: elk --- flowchart TB %% ===== User devices (left rail) ===== subgraph Devices["👥 User devices"] direction TB Golfer(["📱 Golfer phone"]) FieldTester(["📱 Field-tester phone"]) Customer(["🖥️ Customer browser"]) Operator(["🖥️ Operator laptop"]) end %% ===== Cloud ===== subgraph Cloud["☁️ Cloud — Supabase (project: ace_cloud)"] direction TB CPS["cloud-platform-service
9 edge functions · Postgres + RLS
Auth · Storage · pgvector · PostGIS"] end %% ===== Robot (physical) ===== subgraph Robot["🤖 Robot (physical · LAN-coupled boards)"] direction TB subgraph RJ["Nav Jetson — v0-ace00-robot"] direction TB RGS["robot-gateway-service
TS / Deno · :8089
phone + cloud gateway
publishes @valeron/gateway-contracts (in-repo workspace pkg)"] RSS["robot-supervisor-service
Python · :8080 · rule engine + autonomy surface
persona: Janice"] AD(["ace_devel :: nav stack
C++ ace binary · :8090
Jimmy · external"]):::extnode HAL(["ace_devel :: HAL
motor / sensor / battery drivers
Jimmy · external · no external callers today"]):::extnode ACED(["ace_dashboard — robot-services gateway
fronts HAL + nav + …
planned · Jimmy · external · port TBD"]):::extnode MFA["mobile-field-app
Svelte · served :9000
nav-mode commands · /log · /healthz"] end subgraph CJ["Capture Jetson — v0-ace00-capture"] direction TB RCS["robot-capture-service
TS / Deno · :8100 (webhook) · :8101 (pipeline callback)"] PIPE(["golfer — capture pipeline
dora · Brandon + Marius · external"]):::extnode end end %% ===== Mobile native ===== subgraph MobileNative["📱 Mobile (native)"] MGA["mobile-golfer-app
React Native / Expo"] end %% ===== Desktop ===== subgraph DesktopSub["💻 Desktop"] DFA["desktop-ferry-app
TS CLI (future Tauri)"] DTA["desktop-tuner-app
nav2.yaml tuner"] end %% ===== Web (browser-hosted) ===== subgraph WebSub["🌐 Web (browser-hosted)"] WPA["web-platform-app
Next.js
app.valeron.ai (golfer)
admin.valeron.ai (employee)"] WMA["web-mapper-app
Next.js · kyt.valeron.ai"] WMS["web-marketing-site
valeron.ai"] end %% ===== Shared / Docs ===== %% NOTE: shared-gateway-protocols was collapsed 2026-04-28 — the canonical TS %% schema package is now @valeron/gateway-contracts, a workspace package %% inside robot-gateway-service/packages/contracts/, not its own repo. subgraph SharedSub["📚 Shared"] DP["docs-platform
apis/ · decisions/ · diagrams/"] end %% --- User → apps (device hosts app) --- Golfer -. runs .-> MGA FieldTester -. browser .-> MFA Customer -. browser .-> WPA Customer -. browser .-> WMA Customer -. browser .-> WMS Operator -. runs .-> DFA Operator -. runs .-> DTA %% --- Phone (native golfer app) → Cloud: OAuth + pairing --- MGA --"HTTPS · Supabase Auth
+ edge funcs (pair, connect, start-session, end-my-session)"--> CPS %% --- Phone (native golfer app) → Gateway: live shots --- MGA =="WS :8089/ws?session=…
HTTP :8089 /shots · /shots/:id/{video,thumbnail}"==> RGS %% --- Web apps → Cloud --- WPA --"HTTPS · Supabase JS SDK (RLS) + edge funcs"--> CPS WMA --"HTTPS · service-role · publishes course_features"--> CPS %% --- Gateway ↔ Supervisor (on-robot localhost) --- RGS <-- "HTTP localhost · narrow nav-state contract" --> RSS %% --- Gateway → Cloud cloud_sync --- RGS --"HTTPS POST /functions/v1/sessions
service-role · batch shot upload"--> CPS %% --- Supervisor → Cloud (heartbeats + session lifecycle RPCs) --- RSS --"HTTPS · device heartbeats + end-session"--> CPS %% --- Capture Jetson → Nav Jetson (LAN, not Tailscale) --- PIPE --"HTTP LAN loopback :8101
Contract 2: asset_saved"--> RCS RCS --"HTTP LAN :8080 /api/capture/enrollment_complete
Bearer CAPTURE_WEBHOOK_SECRET"--> RSS PIPE --"HTTP LAN :8080 /api/shot_tracker/event
shot_complete · Bearer"--> RSS %% --- Supervisor → Gateway (loopback forward of shot_complete) --- RSS --"HTTP localhost :8089/capture/shot_complete
Bearer GATEWAY_INTERNAL_SECRET"--> RGS %% --- Field-tester phone → Supervisor (served via Field app) --- MFA --"HTTP :9000 → nav-mode commands"--> RSS %% --- Desktop tools → Robot + Cloud --- DFA --"Tailscale HTTP · pull sessions/shots from Supervisor"--> RSS DFA --"HTTPS · upload to /functions/v1/sessions"--> CPS DTA --"Tailscale SSH → curl localhost:8090
nav2.yaml tune + ace_app.service restart"--> AD %% --- Supervisor → nav stack (on-robot) --- %% This solid edge goes away when ace_dashboard ships — RSS migrates to WS via ACED. %% Confirmed by Jimmy 2026-04-19: "you don't need to go to 8090 anymore". RSS --"HTTP :8090 · nav commands
(shared memory for sensors)
transitional — superseded by RSS → ACED once ace_dashboard ships"--> AD %% --- ace_dashboard (planned, Jimmy) — forthcoming transport swap for both clients --- MFA -. "planned · WS · JSON-over-WS (proto later)
prereqs: ACL-108 (Svelte+TS) + ace_dashboard ship" .-> ACED RSS -. "planned · WS · supersedes :8090
prereqs: ace_dashboard ship + supervisor transport refactor" .-> ACED ACED -. "planned · fronts nav" .-> AD ACED -. "planned · fronts HAL" .-> HAL %% --- Contract ownership (dotted, orange) --- %% Note: @valeron/gateway-contracts no longer surfaced as its own node — the %% schemas it owns (Gateway↔Phone, Gateway↔Supervisor, Gateway↔Cloud) are %% an internal package of robot-gateway-service (folded 2026-04-28). DP -.->|hosts wire contracts for| CPS DP -.->|hosts wire contracts for| RCS DP -.->|hosts wire contracts for| RSS RGS -.->|publishes @valeron/gateway-contracts consumed by| MGA %% --- Classes --- classDef cloud fill:#0a1a24,stroke:#60a5fa,color:#dbeafe; classDef robot fill:#0e1a12,stroke:#9ac76a,color:#d7e7c5; classDef mobile fill:#1a0f1e,stroke:#c084fc,color:#e9d5ff; classDef desktop fill:#1a1a0e,stroke:#eab308,color:#fef3c7; classDef web fill:#0e1a1a,stroke:#22d3ee,color:#cffafe; classDef shared fill:#1a120e,stroke:#f97316,color:#fed7aa; classDef device fill:#111,stroke:#64748b,color:#cbd5e1; classDef extnode fill:#1a1a1a,stroke:#6b7280,color:#d1d5db,stroke-dasharray:5 5; class CPS cloud; class RGS,RSS,MFA,RCS robot; class MGA mobile; class DFA,DTA desktop; class WPA,WMA,WMS web; class DP shared; class Golfer,FieldTester,Customer,Operator device; class PIPE,AD,HAL,ACED extnode; %% Subgraph styling style Cloud fill:#05121c,stroke:#60a5fa,color:#dbeafe style Robot fill:#081408,stroke:#9ac76a,color:#d7e7c5 style RJ fill:#0c1f12,stroke:#9ac76a,color:#d7e7c5 style CJ fill:#0c1f12,stroke:#9ac76a,color:#d7e7c5 style MobileNative fill:#140a17,stroke:#c084fc,color:#e9d5ff style DesktopSub fill:#14130a,stroke:#eab308,color:#fef3c7 style WebSub fill:#0a1414,stroke:#22d3ee,color:#cffafe style SharedSub fill:#140f0a,stroke:#f97316,color:#fed7aa style Devices fill:#0b0b0d,stroke:#64748b,color:#cbd5e1

Thick double lines (==>) are the phone-to-Gateway path that Feature #7 introduced (Phase 3+4 of the rollout, shipped 2026-04-21). Dotted orange arrows mark where contract source-of-truth lives. Dashed nodes are owned outside Valeron but run on our hardware: ace_devel (Jimmy's nav stack, Nav Jetson) and golfer (Brandon + Marius's capture pipeline, Capture Jetson).

Protocol legend

Colors used in the diagram + in the contract table below.

HTTP
On-robot LAN + loopback · bearer-secret gated
HTTPS
Supabase edge funcs + REST · JWT or service-role
WebSocket
Phone ↔ Gateway live push (new_shot)
Tailscale
Operator + ferry reach Robot Jetson over mesh VPN
Local-only
Loopback / Unix socket inside a single host
None
Deliberately pull-only — cloud never pushes to robot

Contract catalog

Every contract that crosses a process or host boundary, where it lives, which surfaces it joins.

Contract Boundary Protocol Auth Source of truth
Cloud edge functions
pair · start-session · end-session · end-my-session · active-device-for-user · users-by-device · sessions · courses · connect
Client (phone, web, robot, ferry) ↔ Cloud HTTPS Supabase JWT (user) or service-role (robot, ferry) docs-platform/apis/cloud/edge-functions/*.yaml
Capture → Gateway webhook
enrollment_complete
Capture Jetson → Nav Jetson (Gateway) HTTP (LAN) Bearer CAPTURE_WEBHOOK_SECRET docs-platform/apis/webhooks/capture-to-gateway/enrollment_complete.md
Capture → Supervisor webhook
shot_complete
Capture Jetson → Nav Jetson (robot-supervisor-service) HTTP (LAN) Bearer CAPTURE_WEBHOOK_SECRET docs-platform/apis/webhooks/capture-to-janice/shot_complete.md (path retains the capture-to-janice slug — Janice is the persona name, robot-supervisor-service is the canonical service)
Supervisor → Gateway shot_complete forward
POST /capture/shot_complete
Loopback on Nav Jetson HTTP loopback Bearer GATEWAY_INTERNAL_SECRET @valeron/gateway-contracts v0.2.0 (in robot-gateway-service/packages/contracts/; ACL-104)
Phone → Gateway live feed
GET /ws · GET /shots · GET /shots/:id/{video,thumbnail}
Golfer phone (LAN / AP) ↔ Gateway WS + HTTP Session token (?session=… on WS; Authorization on HTTP) @valeron/gateway-contracts v0.2.0 (in robot-gateway-service/packages/contracts/; ACL-104)
Capture Jetson internal
pipeline ↔ capture service (Contract 1 + 2)
Two processes, same host HTTP loopback + Unix sockets HMAC-shared-secret (Contract 1) / bearer (Contract 2) docs-platform/apis/capture/{internal-http,unix-sockets}.md
Robot onboarding Field-tester / operator phone ↔ Robot Jetson HTTP Bluetooth-provisioned creds (future) docs-platform/apis/robot/ONBOARD.md
Gateway → Cloud cloud_sync
batched shot uploads
Nav Jetson (Gateway) → Cloud HTTPS Service-role key (SUPABASE_SERVICE_ROLE_KEY) Reuses apis/cloud/edge-functions/sessions.yaml
Operator ↔ Supervisor
desktop-ferry-app
Operator laptop ↔ Nav Jetson (robot-supervisor-service) Tailscale Tailscale identity (mesh VPN) Per-tool README (no cross-surface contract)
Operator ↔ ace nav stack
desktop-tuner-app ("Jamal Tooner")
Operator laptop ↔ Nav Jetson (ace_devel, ace_app.service) Tailscale SSH → curl :8090 Tailscale identity (mesh VPN) Per-tool README (no cross-surface contract)
Field app ↔ ace_dashboard (planned)
forthcoming transport swap from today's HTTP :9000 → :8090
mobile-field-app ↔ ace_dashboard (Nav Jetson) WS TBD TBD · JSON-over-WS (proto later) per Jimmy · not yet ticketed
Supervisor ↔ ace_dashboard (planned)
forthcoming transport swap from today's HTTP :8090
robot-supervisor-service ↔ ace_dashboard (Nav Jetson) WS TBD TBD · same JSON-over-WS shape as MFA · not yet ticketed · confirmed by Jimmy 2026-04-19 ("you don't need to go to 8090 anymore")
Cloud → Robot push Cloud → Robot none n/a docs-platform/apis/webhooks/cloud-to-robot/README.md — explicitly documents the pull-only policy

Repositories

Every Valeron-maintained repo + external-owned repos we consume, colored by surface. Names per ADR-022 (Phase B–E renames complete).

cloud-platform-service

surface: cloud · Supabase-hosted · project name: ace_cloud
Supabase backend: auth, pairing, sessions, course data. 9 edge functions + RLS-gated Postgres.
owns: apis/cloud/edge-functions/*.yaml

robot-supervisor-service

surface: robot · Nav Jetson (v0-ace00-robot) · Python · :8080 · unit: robot_supervisor_service.service · path: /opt/valeron/supervisor/
Rule engine, armed/disarmed gate, geofence veto, nav-mode coordination, demo-sequencer module, future autonomy surface. Persona: Janice (preserved in unit Description, log prefix, alert templates).
consumes: @valeron/gateway-contracts (npm-style import from robot-gateway-service workspace), apis/webhooks/capture-to-janice/shot_complete.md

robot-gateway-service

surface: robot · Nav Jetson (v0-ace00-robot) · TS / Deno · :8089
Phone + cloud HTTP gateway. Owns shot_store, WS broadcast, video proxy, cloud_sync, golfer SPA static serving. Carved from ace_supervisor via Feature #7 (Phase 6 Track A landed 2026-04-21). Hosts the @valeron/gateway-contracts workspace package at packages/contracts/.
publishes: @valeron/gateway-contracts v0.2.0 (canonical schemas for Gateway↔Phone, Gateway↔Supervisor, Gateway↔Cloud cloud_sync)

robot-capture-service

surface: robot · Capture Jetson (v0-ace00-capture) · TS / Deno · :8100 + :8101
Capture Jetson enrollment + shot webhook bridge between pipeline and Supervisor.
owns: apis/capture/*.md · sends: apis/webhooks/capture-to-gateway/enrollment_complete.md + apis/webhooks/capture-to-janice/shot_complete.md

mobile-golfer-app

surface: mobile · React Native / Expo · golfer phone
Golfer companion app. Pilot on hold pending Apple Dev License; Expo identity (ace-pilot) intentionally deferred.
consumes: edge funcs + @valeron/gateway-contracts (WS/HTTP, vendored from robot-gateway-service)

mobile-field-app

surface: mobile · served from Nav Jetson · :9000 · Svelte+TS (per ADR-021) · unit: mobile_field_app.service · path: /opt/valeron/fieldapp/
Phone-browser ops UI for field testers. Served on the robot, not published to App Store. Underscored unit name + bare-word dir per ADR-022 Amendment 2026-04-22/23.
consumes: Supervisor internal HTTP (no cross-surface contract)

web-platform-app

surface: web · Next.js · app.valeron.ai + admin.valeron.ai
Customer portal (golfer dashboard + admin console).
consumes: Supabase JS SDK + apis/cloud/edge-functions/

web-mapper-app

surface: web · Next.js · kyt.valeron.ai
Course-mapping web app for geofences + robot paths (KYT).
consumes: Supabase service-role → course_features publish flow

web-marketing-site

surface: web · valeron.ai
Brand / marketing site. Standalone — no platform integrations.

desktop-ferry-app

surface: desktop · TS CLI · future Tauri · operator laptop
One-way ferry: pulls sessions/shots from robot-supervisor-service over Tailscale, uploads to Cloud.
consumes: Supervisor pull endpoints + apis/cloud/edge-functions/sessions.yaml

desktop-tuner-app

surface: desktop · operator laptop · "Jamal Tooner"
nav2.yaml tuning tool for on-robot nav params.
consumes: ace_devel nav stack via Tailscale SSH → curl localhost:8090; restarts ace_app.service systemd unit. Does not route through robot-supervisor-service.

docs-platform

surface: docs · diagrams.valeron.ai
Platform documentation, ADRs, wire-contract specs for non-shared surfaces, all diagrams.
hosts: apis/cloud/, apis/robot/, apis/capture/, apis/webhooks/

golfer (Brandon + Marius)

external · runs on Capture Jetson (v0-ace00-capture) · dora pipeline
Shot tracker pipeline (perception, shot capture, appearance enrollment). Runs next to robot-capture-service, owned outside Valeron.
sends: apis/capture/internal-http.md (Contract 1 + 2) · downstream via robot-capture-service to capture-to-gateway/enrollment_complete.md + capture-to-janice/shot_complete.md

ace_dashboard (Jimmy · planned)

external · planned on Nav Jetson · no repo yet · port TBD
Robot-services gateway. Encapsulates all calls to robot services (HAL, nav, etc.) behind a single service boundary. mobile-field-app will swap its transport from direct HTTP to the Supervisor (:9000 → :8090) for a WS connection against ace_dashboard once it ships.
planned contract: MFA ↔ ace_dashboard WS (JSON-to-start, proto later per Jimmy)

ace_devel (Jimmy)

external · runs on Nav Jetson (v0-ace00-robot) · C++ ace binary · :8090
Robot nav stack — sensor pipeline, HAL drivers, nav2 modules, motor commands. Talked to by robot-supervisor-service over localhost; shared memory for live sensor state.
consumes: Supervisor HTTP · no cross-surface contract (all on-robot)

Notes on the current state

Feature #7 has shipped. Phase 6 Track A landed 2026-04-21. mobile-golfer-app talks to the Gateway directly, the golfer web app that used to live at ace_supervisor/ui/golfer/ is retired (not drawn), and the Supervisor's shot_complete branch is a pure loopback forward to Gateway — Supervisor no longer owns shot_store, cloud_sync, or the phone WS channel. Schemas that cross the Gateway boundary live in @valeron/gateway-contracts v0.2.0 — a workspace package at robot-gateway-service/packages/contracts/, not a separate repo (the earlier "shared-gateway-protocols" name from the F#7 design doc never became a real package).

Janice is the persona, not the canonical name. The supervisor service was historically called Janice, and the persona is preserved in the systemd unit Description, log prefix, and alert templates. The repo + service identifier is robot-supervisor-service / robot_supervisor_service.service per ADR-022 Amendment 2026-04-22 (Jetson-side rename landed 2026-04-23 via ACL-185). Anywhere this diagram says "Supervisor" + the persona note, that's the same thing.

Two-Jetson topology. The robot is a two-board system: Nav Jetson (Tailscale hostname v0-ace00-robot as of 2026-04-22 reflash; runs the C++ nav stack, robot-supervisor-service, robot-gateway-service, mobile-field-app) and Capture Jetson (v0-ace00-capture; runs the shot-tracking pipeline + robot-capture-service). Both sit physically on the robot — the historical "Robot Jetson" label is misleading. They're connected over an eth1 RJ45 cable on subnet 192.168.50.0/24 (.10 Nav, .11 Capture, per ADR-023). On-course operation is internet-free; cloud sync is queue-and-forward.

What's deliberately absent. No cloud-to-robot push exists, by design (robots are NAT'd, connectivity-variable, and on-course typically offline per ADR-023). The apis/webhooks/cloud-to-robot/ doc exists to enshrine the pull-only policy. Observer UI / Supervisor UI for operators is elided — per ADR-021 it migrates cloud-hosted, framework TBD, not yet a standalone repo.

ace_dashboard — planned, not shipped. Jimmy's forthcoming service on the Nav Jetson that fronts HAL + nav (and presumably other robot-services) behind one boundary. Dashed node in the Nav Jetson subgraph. Both Nav-Jetson clients of nav today migrate through it: (1) the Field-app transport swap (HTTP :9000 → RSS :8090 becomes WS → ace_dashboard), a one-file change in mobile-field-app/src/lib/stores/transport.js bundled with ACL-108 (Svelte+TS migration); (2) the Supervisor's own nav-stack calls (today RSS → :8090 on the C++ ace binary) also move to WS via ace_dashboard — confirmed by Jimmy 2026-04-19 ("you don't need to go to 8090 anymore"). Neither migration is ticketed yet — both wait on ace_dashboard shipping. Feature #7 Phase 5 Step 5 (relocating mobile-field-app into Gateway) was cancelled 2026-04-19: same-origin collapse was a golfer-phone / Safari-HTTPS driver, and Field Control runs over Tailscale so the constraint doesn't apply. mobile-field-app stays in its own repo, own port, own systemd unit.

HAL surfaced as its own node. Per Jimmy's framing, ace_dashboard "encapsulates all calls to robot services (HAL / nav / etc)" — HAL is a named upstream by definition, so surfacing it separately from the nav stack keeps the diagram honest about what ace_dashboard is fronting. Today HAL has no external callers (it's accessed intra-binary inside ace_devel); the only inbound edges on the diagram are the dashed planned edges from ace_dashboard. This is the right state: HAL becomes reachable from outside the ace binary exactly when ace_dashboard ships.

Identity axes to remember. Repo name ≠ Supabase project name ≠ env-var key ≠ persona name. cloud-platform-service (the repo) drives the ace_cloud Supabase project (preserved), which is addressed in consumer repos via ACE_CLOUD_* env vars (preserved). robot-supervisor-service (the repo + unit) carries the Janice persona (preserved in description/logs/alerts). See ECOSYSTEM.md §Repository Index for the multi-axis carve-out pattern.

Ground truth: docs-platform/architecture/ECOSYSTEM.md (repo enumeration), docs-platform/architecture/FLOWS.md (12 product flows; cite a flow before changing wiring), docs-platform/apis/ (contract sources), cloud-platform-service/docs/plans/2026-04-16-gateway-extraction.md (Feature #7 design doc), docs-platform/decisions/023-on-course-network-topology.md (ADR-023 — internet-free operation), docs-platform/decisions/022-repo-renames.md Amendment 2026-04-22 (underscored unit names). When this diagram and those sources disagree, the sources win — this file is a snapshot.