Skip to content

Commit 0b661b9

Browse files
docs: снята ложная предпосылка «musl-Bun не резолвит DNS»
Обобщение из замера 08.10 (getaddrinfo ETIMEOUT на www.moltbook.com) разошлось по пяти местам: три комментария в ynison.js, KDoc proxyEnv, пример формата и убеждение в SELF-IMPROVEMENT.md. На нём стоял баг 26.10.2026: state-сокет шёл на IP фронтенда вместо адреса шарда и молчал 10 с. Замер 26.10: dns.lookup шарда под run-as резолвит. Настоящая причина proxyEnv - IPv4-first CONNECT (DNS отдаёт IPv6, bun не делает fallback). ROADMAP уже описывал снятие мифа, его не трогал. Поведение не меняется: только комментарии и датированные утверждения.
1 parent cab6340 commit 0b661b9

3 files changed

Lines changed: 27 additions & 16 deletions

File tree

‎app/src/main/assets/mcp/ynison.js‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@ const SELF_ID = process.env.YNISON_DEVICE_ID || "opencode-mobile";
5252
// is written into opencode.jsonc (as {env:...}) and is the only thing standing between a
5353
// random app on the device and a full Yandex.Music session. Two secrets, two env vars.
5454
const MCP_TOKEN = process.env.MCP_YNISON_TOKEN || "";
55-
// musl-Bun on Android cannot resolve DNS at all (no /etc/resolv.conf, no netd bridge),
56-
// so the app resolves these hosts in Java and hands us IPs. We then dial the IP and send
57-
// the real hostname as the `Host` header. Measured on-device: a plain hostname fetch dies
58-
// with `getaddrinfo ETIMEOUT` in ~5s, the IP+Host form answers in ~0.3s.
55+
// The app resolves these hosts in Java and hands us IPs as a FALLBACK; we then dial the IP
56+
// and send the real hostname as the `Host` header. This used to be justified with "musl-Bun
57+
// on Android cannot resolve DNS at all" - that generalisation was wrong and cost a bug on
58+
// 26.10.2026: `dns.lookup` in the same bun resolved the shard to 77.88.48.156, while the
59+
// state socket kept dialling an IP taken from the handshake and the frontend stayed silent
60+
// for 10s. The IP plumbing stays because the redirect address still arrives from Java
61+
// (`RESOLVED_*_IP`); the state socket resolves its own shard (see resolveStateHost).
5962
const YNISON_HOST = "ynison.music.yandex.ru";
6063
const API_HOST = "api.music.yandex.net";
6164
const RESOLVED_YNISON_IP = process.env.YNISON_WS_IP || "";
@@ -147,8 +150,8 @@ function version() {
147150
// A minimal WebSocket client over Bun.connect, replacing the built-in WebSocket for the
148151
// Ynison sockets. Both failures it works around were measured on the device, not guessed:
149152
//
150-
// 1. musl-Bun cannot resolve DNS at all, so the URL would have to carry an IP. An IP URL
151-
// makes the built-in client put that IP in the TLS SNI, and nginx (the front for
153+
// 1. The address we dial may be an IP (the app hands one over as a fallback), and an IP URL
154+
// makes the built-in client put that IP in the TLS SNI. nginx (the front for
152155
// ynison.music.yandex.ru) tears such connections down - `onerror` in ~250ms, while a
153156
// plain `Bun.connect` with `tls.servername` set to the real host handshakes fine.
154157
// 2. Ynison's Sec-WebSocket-Protocol must be sent as a raw header anyway (see
@@ -1096,8 +1099,8 @@ function parseApiResponse(raw) {
10961099
// One HTTPS GET to the catalog API, over Bun.connect rather than fetch.
10971100
//
10981101
// The reason is the same one the WebSocket client above documents, and it was measured on
1099-
// the device: musl-Bun cannot resolve DNS, so the app hands us the API address as an IP, and
1100-
// fetch on an IP URL puts that IP into the TLS SNI. The certificate then does not verify -
1102+
// the device: the app hands us the API address as an IP (fallback), and fetch on an IP URL
1103+
// puts that IP into the TLS SNI. The certificate then does not verify -
11011104
// "unknown certificate verification error" on roughly 1 call in 5, which surfaced as "the
11021105
// agent found no track". tls.servername is the fix, and Bun.connect is the only client here
11031106
// that can set it, so the REST path now uses it instead of living on a retry.

‎app/src/main/java/org/opencode/mobile/server/OpencodeRuntime.kt‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,11 @@ object OpencodeRuntime {
552552
}
553553

554554
/**
555-
* Прокси для musl-Bun. Ресолвер Android доступен из Kotlin, а встроенный musl-Bun
556-
* не резолвит DNS, поэтому его fetch падает с transport-ошибкой (status 0), а не
557-
* с HTTP-кодом. Тот же приём, что у serve ([startServe]), вынесен отдельно: память
555+
* Прокси для musl-Bun. Прежнее обоснование «встроенный musl-Bun не резолвит DNS,
556+
* поэтому его fetch падает с transport-ошибкой (status 0)» оказалось неверным
557+
* (замер 26.10.2026: `dns.lookup` резолвит). Настоящая причина — в [Ipv4Proxy]:
558+
* DNS отдаёт IPv6, сеть устройства его не маршрутизирует, а у bun нет отката на
559+
* IPv4. Тот же приём, что у serve ([startServe]), вынесен отдельно: память
558560
* и музыки наружу не ходят и прокси не получают.
559561
*/
560562
private fun proxyEnv(): Map<String, String> {

‎docs/SELF-IMPROVEMENT.md‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ kill-dates на несущие»).
4747
### Формат
4848

4949
```markdown
50-
> **belief** · verified: 2026-10-08 · kill: 2026-11-08 · ref: Moltbook
51-
> Молтбук с телефона доступен только через IP+Host-заголовок, потому что DNS на musl-bun отдаёт ETIMEOUT.
50+
> **belief** · verified: 2026-10-26 · kill: 2026-12-26 · ref: `dns.lookup` шарда под run-as
51+
> State-сокет Ynison отвечает только на адресе шарда: фронтенд принимает TLS и молчит.
5252
```
5353

5454
Пример выше разбирается тестом наравне с остальными: он лежит в блоке кода, но
@@ -124,11 +124,13 @@ A (править `filesDir/mem`) отвергнут отдельно: петл
124124
> **belief** · verified: 2026-10-08 · kill: 2026-12-07 · ref: `pm list packages | grep termux`, `/data/data/com.termux`
125125
> Termux на телефоне отсутствует: пакета нет, каталога `/data/data/com.termux` нет.
126126
127-
> **belief** · verified: 2026-10-08 · kill: 2026-11-22 · ref: `getaddrinfo ETIMEOUT`, замер moltdns.mjs 08.10.2026
128-
> DNS в musl-Bun не резолвит: `getaddrinfo www.moltbook.com` → `ETIMEOUT` за 5011мс при активном VPN.
127+
> **belief** · verified: 2026-10-26 · kill: 2026-12-26 · ref: `dns.lookup` шарда под run-as, баг 26.10.2026
128+
> musl-Bun на Android резолвит DNS. Прежняя запись «не резолвит вовсе» (по `getaddrinfo
129+
> ETIMEOUT` 08.10) была неверным обобщением и стоила бага: state-сокет Ynison шёл на IP
130+
> фронтенда вместо адреса шарда и молчал 10 с.
129131
130132
> **belief** · verified: 2026-10-08 · kill: 2026-11-22 · ref: замер moltip.mjs, HTTP 200 за 855мс
131-
> Обход DNS через IP + `Host`-заголовок работает: `fetch("https://143.204.238.10/", {headers:{host:...}})`
133+
> Адресация по IP + `Host`-заголовку работает: `fetch("https://143.204.238.10/", {headers:{host:...}})`
132134
> → HTTP 200.
133135
134136
> **belief** · verified: 2026-10-08 · kill: 2026-12-07 · ref: KDoc MoltbookClient.kt + тик 08.10.2026 01:59
@@ -352,6 +354,10 @@ bun signals-cli.js files/opencode.log --draft --project opencode-mobile
352354
IP+Host тоже работает (HTTP 200 за 855мс). Устарела не сама поломка, а
353355
необходимость обхода: клиент переехал на `HttpURLConnection`, где резолвер
354356
Android работает штатно.
357+
**Дописано 26.10.2026:** обобщение «musl-Bun не резолвит DNS вовсе» оказалось ложным —
358+
`dns.lookup` шарда под `run-as` вернул адрес. На этом обобщении стоял баг state-сокета
359+
(шёл на IP фронтенда, 10 с тишины), поэтому запись выше про ETIMEOUT читать как «в тот
360+
момент с активным VPN так и было», а не как свойство рантайма.
355361
- **«Ticker отложен: петля должна сначала доказать, что не врёт».**
356362
Стало: тикер ходит по расписанию, доказательство даёт журнал-свидетель и сторож.
357363
- **«Как агент узнаёт о своём падении, если лог пишет процесс, а читает агент?»

0 commit comments

Comments
 (0)