Ecosystem map · projected state

Valeron Ecosystem

How the properties connect, where contracts live, and which protocols cross each boundary. Rendered as the system will look after Feature #7 (Gateway Extraction) lands — i.e. once ace_supervisor has been split into robot-supervisor-service, robot-gateway-service, and shared-gateway-protocols.

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

Revised 2026-04-20: Feature #7 Phase E/11 rename target flipped (sequencersupervisor); contract-catalog row split (capture-to-gateway/enrollment_complete + capture-to-janice/shot_complete); two stray Sequencer refs flipped to Supervisor. See ADR-022 Amendment 2026-04-20.

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["Robot Jetson — v0-ace00"] direction TB RGS["robot-gateway-service
TS / Deno · :8089
phone + cloud gateway"] RSS["robot-supervisor-service
Python · rule engine + autonomy surface
(was ace_supervisor)"] 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 ===== subgraph SharedSub["📚 Shared"] SGP["shared-gateway-protocols
TS schema package (ACL-104)"] 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 ↔ Sequencer (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 %% --- Sequencer → Cloud (heartbeats + session lifecycle RPCs) --- RSS --"HTTPS · device heartbeats + end-session"--> CPS %% --- Capture Jetson → Robot 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 %% --- Janice → Gateway (loopback forward of shot_complete) --- RSS --"HTTP localhost :8089/capture/shot_complete
Bearer GATEWAY_INTERNAL_SECRET"--> RGS %% --- Field-tester phone → Sequencer (served via Field app) --- MFA --"HTTP :9000 → nav-mode commands"--> RSS %% --- Desktop tools → Robot + Cloud --- DFA --"Tailscale HTTP · pull sessions/shots from Janice"--> RSS DFA --"HTTPS · upload to /functions/v1/sessions"--> CPS DTA --"Tailscale SSH + HTTP · nav2.yaml tune"--> RSS %% --- Sequencer → 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) --- DP -.->|hosts wire contracts for| CPS DP -.->|hosts wire contracts for| RCS DP -.->|hosts wire contracts for| RSS SGP -.->|typed schemas for| RGS SGP -.->|typed schemas for| MGA SGP -.->|typed schemas for| RSS %% --- 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 SGP,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 Phase 3+4 phone-to-Gateway path that Feature #7 introduces. 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, Robot 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 → Robot Jetson (Gateway) HTTP (LAN) Bearer CAPTURE_WEBHOOK_SECRET docs-platform/apis/webhooks/capture-to-gateway/enrollment_complete.md
Capture → Janice webhook
shot_complete
Capture Jetson → Robot Jetson (Janice) HTTP (LAN) Bearer CAPTURE_WEBHOOK_SECRET docs-platform/apis/webhooks/capture-to-janice/shot_complete.md
Janice → Gateway shot_complete forward
POST /capture/shot_complete
Loopback on Robot Jetson HTTP loopback Bearer GATEWAY_INTERNAL_SECRET shared-gateway-protocols (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) shared-gateway-protocols (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
Robot Jetson (Gateway) → Cloud HTTPS Service-role key (SUPABASE_SERVICE_ROLE_KEY) Reuses apis/cloud/edge-functions/sessions.yaml
Operator ↔ Robot
desktop-ferry-app + desktop-tuner-app
Operator laptop ↔ Robot Jetson Tailscale 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 (Robot 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 (Robot 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 post-Feature-#7 projection.

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 · v0-ace00 · Python
Rule engine, armed/disarmed, demo-sequencer module, future autonomy surface. Post-Feature-#7 Python remainder (was ace_supervisor). Target flipped from robot-sequencer-servicerobot-supervisor-service on 2026-04-20 per ADR-022 Amendment 2026-04-20.
consumes: shared-gateway-protocols, apis/webhooks/capture-to-janice/shot_complete.md

robot-gateway-service

surface: robot · v0-ace00 · TS / Deno · :8089
Phone + cloud HTTP gateway. Owns shot_store, WS broadcast, video proxy, cloud_sync. Carved from Janice in Feature #7.
consumes + typed-by: shared-gateway-protocols

robot-capture-service

surface: robot · v0-ace00-capture · TS / Deno · :8100 + :8101
Capture Jetson enrollment + shot webhook bridge between pipeline and Janice.
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 + shared-gateway-protocols (WS/HTTP)

mobile-field-app

surface: mobile · served from Robot Jetson · :9000 · Svelte+TS (per ADR-021)
Phone-browser ops UI for field testers. Served on the robot, not published to App Store.
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 Janice over Tailscale, uploads to Cloud.
consumes: Janice 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: Janice via Tailscale SSH/HTTP

shared-gateway-protocols

surface: shared · TS package · npm-distributable
Typed schemas for every message crossing the Gateway boundary. New repo from Feature #7 (ACL-104).
canonical for: Gateway ↔ Phone, Gateway ↔ Sequencer, Gateway ↔ Cloud cloud_sync

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 Robot 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 Robot Jetson (v0-ace00) · C++ ace binary · :8090
Robot nav stack — sensor pipeline, HAL drivers, nav2 modules, motor commands. Talked to by the Sequencer over localhost; shared memory for live sensor state.
consumes: Sequencer HTTP · no cross-surface contract (all on-robot)

Notes on the projected state

What's different from today. The diagram shows post-Feature-#7 wiring: mobile-golfer-app talks to Gateway directly, the golfer web app that used to live at ace_supervisor/ui/golfer/ is retired (not drawn), and Janice's shot_complete branch is a pure loopback forward to Gateway — it no longer owns shot_store, cloud_sync, or the phone WS channel. Today, most of those paths still run through Janice.

What's deliberately absent. No cloud-to-robot push exists, by design (robots are NAT'd, connectivity-variable). The apis/webhooks/cloud-to-robot/ doc exists to enshrine the pull-only policy. Observer UI / Sequencer 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 Robot Jetson that fronts HAL + nav (and presumably other robot-services) behind one boundary. Dashed node in the Robot Jetson subgraph. Both Robot-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 Sequencer'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. cloud-platform-service (the repo) drives the ace_cloud Supabase project (preserved), which is addressed in consumer repos via ACE_CLOUD_* env vars (preserved). See ECOSYSTEM.md §Repository Index for the three-axis carve-out pattern.

Ground truth: docs-platform/architecture/ECOSYSTEM.md (repo enumeration), docs-platform/apis/ (contract sources), cloud-platform-service/docs/plans/2026-04-16-gateway-extraction.md (Feature #7 design doc). When this diagram and those sources disagree, the sources win — this file is a snapshot.