Back to Napplet Machines V8
SOURCE / PINNED RELEASE

Made of little things.

Napplet Machines V8

Release
a48ffab2ae1e…
Author-recorded commit
2a817e4d389e…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a

Archive hash verified: ecd5161370161eeb…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

docs/multiplayer-phone-investigation.md
# Published phone room-list timeout investigation

Updated: 25 September 2026. The user clarified that **Android Brave fails while
fetching the room list**, before a room lobby appears. The affected phone's network
(Wi-Fi/cellular) and guest/signed-in state are not yet known. Public desktop and
desktop mobile-emulation listing checks pass; the actual phone failure has not
been reproduced or traced. No game fix or new release is claimed. Game code and
the published release were left unchanged.

## Build and tools

- Published source commit: `534f19acb4ba3c6aa7fce7ab4b6608b7d559f251`.
- Artifact SHA-256: `dd885254f9ebf79c482edd7fa650d35fb62729df87952f4beab7571d4e8c2607`.
  Downloaded the public Blossom HTML and checked its digest against the release.
- soyLI 0.22.0; `soyli doctor --json` reports the installed release current.
- `soyli backend status --json` received a valid public `soy_session` response from
  provider `60517f64548279854ee9a122531e4d1a413e3dc956e2d0a450217420623ca688`
  through `wss://relay.napplet.soy/`. The CLI also logged a transient publish retry;
  the health probe completed successfully. This is not evidence of phone reachability.
- Examined the actual deployed host bundle, not just the sibling host source:
  <https://napplet.soy/assets/player-TbAV7J2E.js>, SHA-256
  `667a7c988a7bd23c28cba651c6a27587d4706e659feb65dc806c23931bdde701`.

## Failing flow, narrowed by the user's phone report

Opening Rooms or pressing Refresh executes `refreshRooms()` (`src/main.ts:660`)
and `Multiplayer.list()` (`src/network.ts:208`):

1. `cvm.callTool(provider, 'soy_session')` identifies the transport actor. On a cold
   host connection, this also waits for MCP initialization before sending the tool
   request.
2. After that succeeds, `cvm.callTool(provider, 'soy_room_list', {napplet, protocol})`
   fetches the list.

**Neither room join nor WebRTC runs in this flow.** `soy_room_join`, `webrtc.open`,
`soy_ice`, ICE negotiation and TURN cannot account for this reported list-fetch
failure. Player count, car physics and race synchronization are also outside this
request path. The earlier broader join investigation below is retained only as
separate test evidence.

The responsible operation is therefore a **room-list flow CVM request**. The
published UI cannot distinguish a failed `soy_session` (including MCP startup)
from a failed `soy_room_list`: both reach the same catch handler and display the
underlying error without an operation label. Calling the exact timed-out MCP tool
`soy_room_list` solely because the UI was fetching rooms would be an unsupported
conclusion.

The inspected deployed host's `CvmConnection.request` uses a 10-second MCP initialization
limit, then a 15-second default request limit (options capped at 25 seconds).
The MCP request timer reports `Request timed out`; its SDK adds the MCP error
context. The user's phrase “MCP request timed out” is reported evidence, not a
captured exception or an exact string match in that bundle. The game forwards SDK
errors (`src/network.ts:181`, `errorText`, and `src/main.ts:660`). The timeout belongs
to the host-managed MCP request path; its underlying cause is not established as a
backend server defect. Relay reachability, browser/session behavior and response
delivery remain unmeasured on the actual phone.

The game passes the same provider, namespace, protocol and tool arguments on both
desktop and phone; there is no mobile-specific room-list branch. It owns an error
labeling gap, but no faulty room-list argument or game-generated deadline has been
identified.

## Observed tests

| Environment | Observed result | What it establishes |
| --- | --- | --- |
| Previously recorded six-client soyLI scenario, local backend and local TURN, 50 ms ±15 ms | 23 assertions passed on rerun after a startup timeout | Local synchronization only; not phone or deployed service qualification |
| Published site, two fresh guest Chromium sessions on this computer/network | Both list rooms; host/create/join and peer connection succeed, 4 assertions | Actual public provider, relay and host path works from this machine |
| Published full-screen player, desktop host + 390×844, touch-enabled Chromium guest | Actual napplet frame 390×844, coarse pointer; join/peer connection succeed, 4 assertions | Mobile layout/input emulation on desktop Chromium, not Safari/iOS, Android hardware or cellular |
| Published host ICE inspection, two fresh guest sessions | 4 assertions pass; selected route includes `relay` | Deployed TURN works from this machine; no physical-phone/cellular claim |
| Published list-only follow-up, two fresh guest contexts, desktop + 390×844 touch emulation | Three list fetches each succeed; 8 assertions pass | Actual public list path works from this computer/network; trace contains only `soy_session` and `soy_room_list`, no WebRTC requests |
| User's Android phone, Brave | User reports room-list fetching fails with “MCP request timed out”; no lobby | Pre-join failure confirmed by user; exact failed MCP request and underlying cause still require a phone trace |

Successful guest request timings, measured at the NAP bridge:

| Operation | Public desktop | Public mobile emulation |
| --- | ---: | ---: |
| Initial `soy_session` (includes cold connection setup) | 1,222 ms | 1,208 ms |
| `soy_room_list` | 214 ms | 217 ms |
| Join's repeated `soy_session` | 218 ms | 216 ms |
| `soy_room_join` | 215 ms | 219 ms |
| `webrtc.open` (includes host `soy_ice`) | 227 ms | 219 ms |

The 25 September list-only follow-up measured cold `soy_session` at 1,148 ms
(desktop) and 1,158 ms (touch emulation); repeated sessions at 204–207 ms, and
all six `soy_room_list` requests at 197–219 ms. This is **not Android Brave testing**.
It creates no rooms, joins no rooms and opens no peer connections. Neither local
network shaping nor viewport emulation reproduces the phone's public-network path.

Browser probes navigate from the soyLI harness to the published napplet.soy URL;
they do not use the harness's local provider alias for those calls. The earlier
join probes created only a diagnostic room, used separate guest contexts, then
left/closed them. The list-only probe performs reads only; soyLI closes its contexts
and temporary preview when the command exits.
The CLI's final *local-preview* connection-diagnostics hook cannot inspect those
navigated frames and warns that diagnostics are unavailable; the public scenario
assertions and separately instrumented native ICE route observations remain valid.
The first mobile probe used the embedded listing frame, whose available height
was insufficient for the game. The reported mobile results use the full-screen
player. Locator/bootstrap timeouts during harness setup were not MCP failures.

The existing soyLI tools were used, with temporary instrumentation under the ignored
`.napplet-space/diagnostics/` directory. No signer keys, TURN credentials, SDP,
candidate addresses or encrypted message bodies were captured. Trace files retain
only operation names, correlation IDs, elapsed times, errors, ICE URL configuration
and candidate *types*. No debug logging was added to shipped game code.

Useful local commands/artifacts:

```sh
soyli backend status --json
soyli doctor --json
soyli multiplayer .napplet-space/diagnostics/public-list.mjs --players 2 --timeout 120
soyli multiplayer .napplet-space/diagnostics/public-join.mjs --players 2 --timeout 150
DIAG_MOBILE=1 DIAG_FULL_PLAYER=1 soyli multiplayer .napplet-space/diagnostics/public-join.mjs --players 2 --timeout 150
DIAG_ICE=1 DIAG_FULL_PLAYER=1 soyli multiplayer .napplet-space/diagnostics/public-join.mjs --players 2 --timeout 150
```

The temporary harness reads the existing local release journal; it is a debugging
artifact, not a portable CI test. Preserved evidence files are
`public-list-trace.json`, `public-desktop-success.json`, `public-mobile-success.json`,
`public-ice-success.json` and their logs in that directory.

## Host limits and responsibility

The relevant limitation is the host-owned encrypted MCP transport via
`wss://relay.napplet.soy/`, including connection startup, request deadlines and
delivery to/from the backend provider. This project has no backend/relay operator
logs or connection to the physical phone's browser. Existing soyLI Connection
diagnostics focus on WebRTC peers and cannot expose an earlier MCP timeout on a
remote phone. A local soyLI health check cannot fill that evidence gap.

The sandboxed game cannot repair the host's relay transport, network reachability
or backend deployment. Adding operation context to errors is possible inside the
project, but would be diagnostic assistance, not a demonstrated transport fix.

Separately, the earlier public ICE inspection contained exactly:

- `turn:napplet.soy:3478?transport=udp`
- `turn:napplet.soy:3478?transport=tcp`

There was no `turns:` endpoint or port-443 fallback. This agrees with
`docs/napplet-backend.md:504`. Networks that block both supplied TURN transports
may fail even after all MCP calls succeed. This is a host/deployment limitation;
the sandboxed game cannot supply its own sockets, peer connections, TURN credentials
or host ICE policy. **This TURN limitation does not apply to the reported
room-list failure; listing never reaches the ICE path.**

MCP connection lifetimes, initialization deadlines, Nostr transport, `soy_ice`
issuance and ICE policy belong to the host/backend. This game owns the room-call
arguments, join flow and error presentation. Its generic error presentation is an
observability gap: adding operation labels could improve a future release, but
would not establish or repair the present transport failure.

No evidence currently justifies changing room arguments, raising timeouts,
reducing player count, changing provider identity, or claiming a fixed phone bug.
No host or backend deployment changes were made from this project.

## Evidence still needed from the failing phone

Already established: Android Brave, failing during room-list fetching, before a
lobby. Still useful: Wi-Fi versus cellular, guest versus signed-in use, approximate
wait duration, and whether a fresh foreground reload changes the result. Compare
listing on Wi-Fi and cellular if both are available, changing one variable at a time.

To identify the exact operation, capture the failing device's NAP request/result
pair with its ID and elapsed time (through remote browser inspection or host-side
instrumentation). For `cvm.request`, retain only `message.method`,
`message.params.name`, result/error and timing:

- Failed `soy_session`, with no `soy_room_list` request: distinguish the host's MCP
  initialization from the subsequent `soy_session` tool request in host logs.
- Successful `soy_session`, followed by failed `soy_room_list`: the list tool is
  the request that timed out.

Then correlate with backend/relay logs: did initialization/the tool request arrive,
was a response emitted, and did the browser receive it?
Do not collect secret transport keys, signing material, SDP or TURN passwords.

Current soyLI connection diagnostics primarily observe established/negotiating
WebRTC peers; local emulation and those peer metrics cannot identify an earlier
MCP timeout on a remote phone. A phone trace or host/operator request log is required
before attributing this specific failure to a component or applying a targeted fix.