← Back to README · ← Networking
The netracer/ Kotlin package makes every running Topos APK a first-class node on the Netracer LAN control plane, streaming live §-slang telemetry over a persistent WebSocket so the v2 graph reflects real substrate content rather than static topology.
| Surface | URL |
|---|---|
| Dashboard | http://<netracer-host>:8787/ |
| Dashboard v2 | http://<netracer-host>:8787/v2 |
| Slang WS | ws://<netracer-host>:8787/ws/slang |
| Graph API | http://<netracer-host>:8787/api/slang/graph |
| File | Role |
|---|---|
NetracerSlangClient.kt |
Persistent OkHttp WebSocket with candidate-URL autoscan, exponential backoff, §BIND/§NEGOTIATE handshake, outbound frame queue (512 frames, drop-oldest). |
NetracerTelemetryBroadcaster.kt |
Bridges existing StateFlows (agentState, trafficLog, substrateStatus, tapeState) into §-slang frames with 250 ms debounce floor. |
On onOpen the client unconditionally sends:
§BIND{node=topos-<androidId8>,role=agent,pack=<slangPack>,version=<appVer>,ts=<ms>}
§NEGOTIATE{node=topos-<androidId8>,caps=fiber|bundle|section|traffic|affect|tape|substrate}
Netracer's slang-control-plane.js consumes these, marks the node as negotiated, and wires it into the graph without any manual §0|BIND step in the client.
| Source | Frame |
|---|---|
babyAI.agentState |
§SECTION{§1,kind=agent_state,summary,affect,self,sigma} |
engine.trafficLog |
§BUNDLE{kind=substrate_traffic,direction,type,model,ptok,ctok,lat,ts,prompt,resp} |
engine.substrateStatus |
§SECTION{§0,kind=substrate_status,engine,model,healthy,lat,tps,n,ptok,ctok} |
holographicTape heartbeat |
§SECTION{§1,kind=tape,dim,episodic,momentum,tickMs} (10 s) |
| Directional strands | `§FIBER{from=agent |
All fields are sanitised: newlines → spaces, " → ', , → · to keep the frame parseable by the control plane's splitter.
MainActivity.onCreate()callsstartNetracerTelemetry(slangPack)— loads the override URL (if any) fromSharedPreferences("topos_netracer")and constructs the node id fromSettings.Secure.ANDROID_ID.NetracerSlangClient.start()spawns a driver coroutine that iteratesDEFAULT_CANDIDATES:ws://<netracer-host>:8787/ws/slang(example; do not hardcode — use discovery or override)ws://10.0.2.2:8787/ws/slangws://192.168.56.1:8787/ws/slang
- First URL that opens within 5 s becomes active. Failures trigger exponential backoff (1 s → 30 s).
NetracerTelemetryBroadcaster.start()launches 4 observer jobs (agent, traffic, substrate, heartbeat).MainActivity.onDestroy()tears everything down cleanly.
§0|NETRACER → show state / url / last event
§0|NETRACER ON → (re-)enable, reconnect
§0|NETRACER OFF → disable and close socket
§0|NETRACER ws://<host>:8787/ws/slang → override URL and reconnect
Commands are intercepted in MainActivity.handleNetracerCommand() before the stimulus reaches BabyToposAI, so they never appear as LLM input.
OFFLINE ──start()──▶ CONNECTING ──onOpen──▶ BOUND ──§NEGOTIATED──▶ NEGOTIATED
│ │ │
└─onFailure──▶ ERROR │ │
└──onClose/onFailure──────┘
│
▼
CONNECTING (backoff)
Exposed as NetracerSlangClient.state: StateFlow<State> — observed by MainActivity for operator log feedback.
Existing res/xml/network_security_config.xml permits cleartext globally (cleartextTrafficPermitted="true"), so ws://<netracer-host>:8787 works out of the box. For production deployments switch to a scoped domain-config or use wss:// with a pinned cert.
Note: some older examples in this repo referenced a specific LAN host 192.168.3.20. That DHCP-hosted address is deprecated in our defaults — avoid hardcoding it. Use discovery (BootActivity) or §0|NETRACER ws://<host>:8787/ws/slang to override.
- Outbound queue:
Channel<String>(512, DROP_OLDEST)— slow netracer will never back-pressure the agent. - Frame ceiling: 4096 chars per frame; longer strings are truncated.
- Emit rate: ≥ 250 ms floor on
agentStateobserver, 10 s heartbeat, event-driven on traffic/substrate. - OkHttp client: shared, 20 s ping interval, no read timeout (WS), 5 s connect timeout.