---
name: codoceh-fold-linux-desktop
description: "Полноценный Linux-десктоп на Samsung Galaxy Z Fold — новое поколение: приложение ru.codoceh.stol (proot-Debian встроен в само приложение, без Termux/AVNC), плюс старое поколение Termux/proot-distro/AVNC на предыдущем Fold — обе описаны здесь; включая буфер обмена, расшаренные Загрузки/Документы, и почему прямой SSH с телефона на mini не работает"
metadata: 
  node_type: memory
  type: project
  modified: 2026-08-24T07:14:07.564Z
  originSessionId: 1f3a6f65-acf1-47fc-be99-74d26d2d0ce8
---

## Новое поколение: Fold8 + ru.codoceh.stol (2026-08-20)

**Новый телефон (Fold8, SM-F971B), новое приложение.** Заменяет весь стек выше
(Termux + `ru.codoceh.desktoplauncher` + AVNC). Одно самодостаточное приложение
`ru.codoceh.stol` (v2.1.3) — proot-Debian 13 встроен в само приложение
(`/data/data/ru.codoceh.stol/files/rootfs`), Termux не установлен и не нужен.
Рендер десктопа — тоже в самом приложении (не через AVNC): открываешь
`ru.codoceh.stol`, видишь XFCE-подобный стол с обоями КодоЦеха, доком и своим
терминалом. Внутри уже стоят XFCE/Wine/VS Code/LibreOffice — образ куда
богаче прежнего.

**Доступ к файлам rootfs с компьютера** — так же, как раньше к Termux, но другой
пакет: `adb shell run-as ru.codoceh.stol <абсолютный путь внутри rootfs>`
работает без специальных разрешений (в отличие от `com.termux`, где `run-as`
падал на `RUN_COMMAND`-разрешении). Так кладутся файлы (CLAUDE.md, скиллы,
память) без открытия самого приложения.

**Выполнить команду можно только через настоящий терминал внутри приложения**
— у `ru.codoceh.stol` нет `RunCommandService`-аналога и никакого exported
API (`dumpsys package ru.codoceh.stol` — только `MainActivity`). Значит
единственный путь — скриншотить и тыкать/печатать в GUI-терминал, как в
любое обычное приложение.

**Грабли этой сессии, по порядку, каждая стоила времени:**

1. **Два дисплея у Fold, и у скриншота, и у тача — разные системы ID.**
   `adb exec-out screencap -p -d <id>` без `-d` даёт битый PNG
   («Multiple displays were found») — нужен ID из `dumpsys SurfaceFlinger
   --display-id` (гигантские UUID вида `4630947123231501204`). А вот
   `adb shell input -d <id> tap` хочет ДРУГОЙ, логический ID (0 или 1) —
   бери из `dumpsys display | grep displayId=`. Спутал один с другим —
   получил `Invalid arguments for display ID`.
2. **Тапы не давали никакого эффекта, хотя экран живой** — потому что стол
   был в режиме **«МЫШЬ»** (трекпад/относительное перемещение), а не
   **«ТАЧ»** (абсолютное касание). Тумблер — пилюля в правом верхнем углу
   стола. Без «ТАЧ» курсор не двигается вообще, будто тачи не долетают.
   Отдельно: активный телефонный звонок поверх экрана — не блокирует тачи
   (это была ошибочная гипотеза), просто совпал по времени с режимом «мышь».
3. **`adb shell input text "строка с ; | { }"` — спецсимволы бьются ДО
   устройства.** `adb shell` склеивает все свои argv через пробел и
   пересобирает как одну команду для шелла телефона — кавычки, которые были
   у тебя в локальном bash, туда не попадают. `;`/`|` внутри `input text`
   превращаются в отдельные команды НА САМОМ ANDROID (не на дефисе типа
   `bash: vendor: команда не найдена`), а не печатаются как текст. **Фикс:**
   заворачивать весь текст в ЛИТЕРАЛЬНЫЕ одинарные кавычки внутри самой
   строки — `adb shell input text "'$cmd'"` — тогда adb передаёт их как
   символы, шелл телефона видит один токен, а строка целиком печатается в
   терминал приложения, где её уже парсит bash самого терминала (там `;`
   работает нормально, это два разных уровня парсинга).
4. **bash кеширует путь команды в текущей сессии.** Удалил/заменил бинарь —
   старый терминал-таб всё равно бьётся в старый путь (`bash: ...: Нет
   такого файла`, хотя файла и не должно быть). `hash -r` перед повторным
   вызовом, или открыть новую вкладку терминала (Файл → Открыть вкладку).
5. **`claude`/`gemini` в образе — заглушки, а не отсутствие пакета.**
   `/usr/local/bin/claude` и `/usr/local/bin/gemini` в этом образе — статичные
   шелл-скрипты, которые ВСЕГДА печатают «native binary not installed» и
   `exit 1`, независимо от того, поставлен ли реальный пакет. `$PATH` кладёт
   `/usr/local/bin` РАНЬШЕ `/usr/bin`, где на самом деле лежат настоящие
   npm-симлинки (`npm config get prefix` тут `/usr`, то есть глобальный bin
   — это `/usr/bin`, а не `/usr/local/bin`). **Фикс:** `rm -f
   /usr/local/bin/claude /usr/local/bin/gemini` — тогда PATH сам находит
   рабочий `/usr/bin/claude` (симлинк на `.../claude-code/bin/claude.exe` —
   да, у настоящего бинарника расширение `.exe` даже на Linux, это не баг).
6. **apt-нода в Debian trixie слишком старая (v20) для `@anthropic-ai/
   claude-code` (нужен ≥22).** NodeSource: `curl -fsSL
   https://deb.nodesource.com/setup_22.x | bash -` затем `apt install -y
   nodejs`.
7. **`npm install -g @anthropic-ai/claude-code` иногда не подтягивает свой
   optionalDependency с платформенным бинарником.** Лечится явным `npm
   install -g @anthropic-ai/claude-code-linux-arm64@<та же версия>` следом.
8. **После USB-переподключения (`adb devices` меняет `transport_id`) —
   incoming `input tap`/`input text` могут молча не долетать, а
   `screencap` временами отдаёт СТАРЫЙ закэшированный кадр** (несколько
   скриншотов подряд идентичны пиксель-в-пиксель, будто ничего не
   произошло, хотя команда реально прошла). Разовый симптом видели один
   раз — `adb devices` кратко вернул `error: device still authorizing`
   между вызовами, следом `transport_id` вырос. **Диагностика:** не верить
   тому, что скриншот «не изменился» — пере-тапнуть/перепечатать и снять
   ЕЩЁ один скриншот через пару секунд перед тем как менять гипотезу
   (координаты/дисплей/режим ТАЧ); в этой сессии именно так выяснилось,
   что ввод всё это время реально доходил.

**Итог сегодняшней раскладки (воспроизводимо для следующего сброса/устройства):**
canon (`/root/.claude/CLAUDE.md`, `/root/.gemini/GEMINI.md`), 53 скилла в
`/root/.claude/skills/`, вся память (293 файла) в
`/root/.claude/projects/-root/memory/` — залито через `run-as` без открытия
приложения. `claude`/`gemini` CLI поставлены и реально работают (проверено
`--version`). Сгенерирован свой ssh-ключ `stol-fold8`
(`/root/.ssh/id_ed25519`), публичный добавлен в `authorized_keys` на mini;
алиас `Host mini` в `/root/.ssh/config`.

**Tailscale в итоге поставлен ВНУТРИ самого Debian-контейнера, не как
Android-приложение** (владелец явно поправил: «наша задача ставить tailscale
не на fold, а на linux который на нём находится»). Обычный `tailscaled` с
tun-интерфейсом внутри proot не поднимется — `/dev/net/tun` там существует
(`crw-rw---- system vpn`), но **открыть его непривилегированному процессу
запрещает сам Android-кернел** (`Permission denied` на `cat
/dev/net/tun`), rootless proot тут ни при чём, root тоже не даёт этого права
без официального VPN-приложения. **Рабочий фикс — userspace-networking
режим** (тот же, что используют для Docker без `--cap-add=NET_ADMIN`):
```
tailscaled --tun=userspace-networking \
  --socks5-server=localhost:1055 --outbound-http-proxy-listen=localhost:1055 \
  --state=/var/lib/tailscale/tailscaled.state > /root/tailscaled.log 2>&1 &
disown
tailscale --socket=/var/run/tailscale/tailscaled.sock up --hostname=stol-fold8-debian
```
Никакого сетевого интерфейса не создаётся — `tailscaled` отдаёт локальный
SOCKS5/HTTP-прокси на `:1055`. Поэтому обычный `ssh mini` по Tailscale-IP
напрямую НЕ работает (висит на таймауте — сети до интерфейса нет), нужен
явный проксирующий клиент. Рабочая связка — `ncat` (`apt install -y ncat`,
обычный `netcat-openbsd` тут не годится, нет SOCKS) + `ProxyCommand` в
`~/.ssh/config`:
```
Host mini
  HostName 100.84.177.25
  Port 2222
  User cebers_mini
  IdentityFile ~/.ssh/id_ed25519
  ProxyCommand ncat --proxy 127.0.0.1:1055 --proxy-type socks5 %h %p
```
Подтверждено реальным прогоном: `ssh mini "echo OK; hostname; uptime"` вернул
живой ответ mini. Персистентность процесса — через идемпотентный блок в
`/root/.bashrc` (проверяет `pgrep -x tailscaled`, если нет — поднимает с теми
же флагами и `disown`); проверено убийством процесса и пересозданием сессии
(`source ~/.bashrc`) — `tailscaled` переподнялся и **переавторизовался из
сохранённого `--state`-файла без повторного похода по ссылке логина**, `ssh
mini` снова прошёл. Устройство `stol-fold8-debian` видно в общем tailnet
(`tail2f8be2.ts.net`) наравне с `cebers-mini`, `macbook-air-cebers` и др.
Ограничение подхода: это привязано к жизни контейнера/процесса, а не к
системному init — если приложение `ru.codoceh.stol` будет убито системой
целиком (не просто свёрнуто), `tailscaled` умрёт с ним, и понадобится новая
интерактивная сессия терминала, чтобы `.bashrc`-хук его поднял снова (без
повторной веб-авторизации, `--state` файл сохраняется на диске).

**Obsidian-вольт (`/storage/emulated/0/Documents/Obsidian Vault`, настроен в
этой же сессии через LiveSync) — это Android shared storage, НЕ примонтирован
внутрь rootfs `ru.codoceh.stol`.** `/root/...` внутри приложения — отдельная
песочница; путь до реального вольта из-под Claude/Gemini в этом контейнере
не виден. Если когда-нибудь понадобится читать/писать в вольт из-под ИИ
внутри «стола» — нужен отдельный мост (как раньше `TMPDIR`-мост Termux), сам
собой не появится.

На телефоне владельца (Samsung Galaxy Z Fold, Android 17) поднят полноценный Linux-десктоп: Termux → `proot-distro` → Debian 13 (trixie) → XFCE, без root. Один тап по нативному Android-приложению `ru.codoceh.desktoplauncher` («Linux Desktop» / «Stop Desktop», см. ниже) поднимает/гасит всё. Установлены: Claude Desktop, VS Code, Obsidian (+LiveSync, синхронизирован с той же CouchDB, что и на Mac), Firefox ESR, Antigravity IDE, Antigravity 2.0, Tailscale (`codoceh-fold` в tailnet), Claude Code CLI, SSH-доступ к mac mini.

**ОБНОВЛЕНО 2026-08-15 — рендер навсегда переведён с Termux:X11 на VNC.**
Причина: Chromium/Electron (Claude Desktop, Antigravity) не получают вообще никакого клавиатурного ввода под Termux:X11 — ни с экранной клавиатуры, ни синтетический XTEST (`xdotool type`) — при этом нативные GTK-приложения (терминал) печатают нормально. Подтверждено контрольным экспериментом: тот же XTEST-ввод в GTK работает, в Electron под Termux:X11 — нет; под `Xtigervnc` (полноценный X-сервер) — работает и там, и там. Причина в реализации Termux:X11, не в конфиге.

Текущая схема запуска (`~/start-gui.sh` на телефоне): `Xtigervnc :1 -geometry 1600x1000 -localhost=1 -SecurityTypes None` → `startxfce4` на DISPLAY=:1. Скрипт сам НЕ открывает AVNC — это делегировано нативному лаунчеру (см. следующий блок), скрипт только поднимает сервер и возвращается. Termux:X11 (приложение) оставлен установленным, но больше не используется.

**ОБНОВЛЕНО 2026-08-15 (позже в тот же день) — написан нативный лаунчер `ru.codoceh.desktoplauncher`, полностью решена проблема ненадёжного одно-тапового запуска.**
Исходный подход («открыть AVNC через `am start` с парой `sleep`+ретраев после старта Xtigervnc») давал 30+ секунд и не всегда успешное подключение — недостаточно для «работало безотказно». Финальное решение — Java-приложение (Android Studio + Gradle, уже настроенный toolchain на MacBook, см. [[android-dev-setup-macos-tahoe]]), два launcher-ярлыка на самом Android (НЕ Termux:Widget):
- **`MainActivity`** («Linux Desktop»): запускает `start-gui.sh` через Termux `RunCommandService` (требует `allow-external-apps=true` в `termux.properties` + permission `com.termux.permission.RUN_COMMAND`), затем в фоновом потоке **реально опрашивает `127.0.0.1:5901`** — TCP-коннект + чтение ПОЛНОГО 12-байтного RFB-баннера, каждые 500мс, до ~30с таймаута — вместо слепого `sleep`. Только потом открывает AVNC intent'ом (`vnc://127.0.0.1:5901`, класс `com.gaurav.avnc.UriReceiverActivity`), с одним подстраховочным повторным открытием через 5с (см. грабли — почему без «прогревочных» повторных коннектов).
- **`StopActivity`** («Stop Desktop»): запускает `stop-gui.sh`, затем САМА (будучи настоящей foreground-активностью) переходит на домашний экран (`ACTION_MAIN`+`CATEGORY_HOME`) — надёжнее, чем `am start ... HOME` изнутри фонового процесса Termux (см. грабли ниже).
- Требует `INTERNET` в манифесте — Android гейтит ЛЮБОЙ сокет, включая loopback, этим permission'ом.
- **Результат после проверки НАСТОЯЩИМИ тапами по экрану (не `adb shell am start` по имени компонента!), 2026-08-15:** 4/4 подряд реальных тапа по иконке «Linux Desktop» — подключение за ~15с (включая явную задержку опроса), без единой записи `Blacklisted`/`reset by peer` в логе. Первая попытка «доверять `adb shell am start` как прокси для реального тапа» дала ложное 12/12 — см. грабли про разницу между ними. Батарея: AVNC простаивает ~0% CPU без активной сессии (замерено по `/proc/<pid>/stat`), `stop-gui.sh` реально убивает Xtigervnc+XFCE (подтверждено по `pgrep` и состоянию `/proc/net/tcp`), нет висящих wakelock'ов.
- **Мышь/тач:** по жалобе владельца («отслеживается мышка, стало прям касание») в самих настройках AVNC (Настройки → Ввод → Стиль жестов) переключено с «Сенсорный экран» на «Сенсорная панель» — курсор теперь отслеживается relative-режимом (как тачпад ноутбука), а не телепортируется под палец. Настройка живёт в закрытых (non-debuggable) SharedPreferences AVNC — менять можно только через UI, не через adb/файлы напрямую.
- `Termux:Widget` (приложение) и его `~/.shortcuts/*` теперь не нужны для рабочего пути (оставлены установленными, можно удалить, если важно свести зависимости к минимуму).

**ОБНОВЛЕНО 2026-08-15 — `~/.claude/skills/` (54 скилла) и память (`~/.claude/projects/-Users-cebers-----------/memory/`, 292 файла) перенесены на телефон.**
На телефоне лежат по `/root/.claude/skills/` и `/root/.claude/projects/-root/memory/` — файлово Claude Code внутри Debian на телефоне теперь ВИДИТ те же скиллы и ту же память, что и на MacBook. Это **одноразовый снепшот, не живая синхронизация** (та же логика, что и карточки сессий Claude Desktop, см. ниже) — правки скиллов/памяти на MacBook ПОСЛЕ этой даты сами на телефон не попадут, нужно будет переносить заново. Важное расхождение: скиллы/память писались для путей MacBook (`~/Documents/Obsidian Vault/...`, `~/Разработки/...`) — на телефоне Obsidian vault лежит по другому пути, `/root/ObsidianVault/Projects/<name>/` (без пробела, без `Documents/`), см. подробности в CLAUDE.md телефона §9.

**⚠️ БЛОКЕР, найден при проверке 2026-08-15: сам Claude Code CLI на телефоне сейчас НЕ ЗАПУСКАЕТСЯ — и это, похоже, НЕ проблема телефона.** Реальный тестовый вызов (`claude -p "..."` внутри Debian-контейнера) вернул: `Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access`.
- `claude doctor` подтвердил: установка (2.1.232) в порядке, реально залогинен (есть `oauthAccount` в `.claude.json`).
- **Сверил аккаунт из `.claude.json` на телефоне И на MacBook — идентичен на обеих машинах:** `cebers227@gmail.com`, организация **`S M Life`** (`organizationType: claude_team`, `seatTier: team_bendep_nonprofit_premium`, `billingType: stripe_subscription`, подписка создана `2026-08-01`). Значит дело не в том, что на телефон залогинился «не тот» аккаунт — аккаунт один и тот же.
- **Гипотеза (не проверена):** это НЕ специфично для телефона как устройства, а свежая политика организации `S M Life` — «отключить подписочный доступ к Claude Code», введённая, вероятно, вокруг создания этой team-подписки 2026-08-01. Текущая рабочая MacBook-сессия Claude Code (эта самая) не наткнулась на блок просто потому, что уже была запущена/аутентифицирована ДО того, как её заново проверили против политики — новый (`claude -p ...`) вызов на телефоне попал на свежую проверку и получил отказ. **Если гипотеза верна — это может ударить и по будущим НОВЫМ сессиям Claude Code на самом MacBook, не только на телефоне.** Не проверено эмпирически (не пробовали намеренно оборвать текущую Mac-сессию и запустить новую).
- Файлово всё готово (skills+память видны на телефоне) для реального «агентского редактирования проектов через Claude Code».
- **РЕШЕНО 2026-08-15 (проще, чем казалось):** владелец сам проверил и сообщил, что дело было не в org-политике `S M Life`, а просто в том, что на телефоне не был выполнен `claude login` (не авторизован). Гипотеза про блокировку организации выше осталась НЕ подтверждённой и, судя по всему, ошибочной — не тратить время на неё повторно, если этот блокер всплывёт снова, сначала проверить именно `claude auth` / `claude login` статус.

**ОБНОВЛЕНО 2026-08-15 — обои КодоЦех дополнены брендингом и двумя QR-кодами (сайт + Telegram).**
Финальный источник и генератор: `~/Разработки/CehoIsland/resources/brand/wallpaper/wallpaper-codoceh-1600x1000.png` + `generate_wallpaper.py` (Python, Pillow + `qrencode` CLI) — **это первый реальный источник обоев на MacBook** (раньше его не было вообще, финальный PNG существовал только на телефоне). Состав: логотип (уменьшенная версия мастер-файла `/Users/cebers/Downloads/иконка кодоцех.png`, 1254×1254) + заголовок «КодоЦех» + слоган «Выковано в КодоЦех» + два QR-кода (`https://codoceh.ru` и `https://t.me/CodoCeh`) на белых карточках с логотипом-бейджем по центру каждого. Цвета — актуальная палитра КодоЦех `#2F8E6B`/`#2E8C66` (владелец подтвердил при выборе между двумя найденными в памяти версиями палитры).
- **Критично: канва обоев ДОЛЖНА быть landscape 1600×1000, а не portrait.** Реальное разрешение Xtigervnc — `-geometry 1600x1000` (см. `start-gui.sh`), landscape. XFCE рисует обои в режиме `image-style=5` («zoom» — масштаб с обрезкой по недостающей стороне, сохраняя пропорции). Если взять portrait-картинку (например старые 1440×2560) и просто ДОБАВИТЬ контент ниже существующего лого — новый контент окажется физически ниже видимой зоны и будет обрезан вообще без предупреждения на этапе сборки картинки — баг проявляется ТОЛЬКО при реальном рендере на устройстве, не на скриншоте самого PNG-файла. Была именно эта ошибка первой версии (портретная 1440×2560, QR-коды полностью пропали с экрана) — исправлено пересборкой в родном landscape 1600×1000, что совпадает с реальным разрешением сервера 1:1 и полностью убирает обрезку.
- **QR-код + логотип по центру может СЛОМАТЬ сканируемость непредсказуемо по конкретным данным, даже при одинаковом размере логотипа и версии QR.** У `https://codoceh.ru` и `https://t.me/CodoCeh` — одинаковая QR-версия (v3, 29×29 модулей) и один и тот же процент перекрытия логотипом, но при 26-30% ширины логотипа `codoceh.ru`-код переставал сканироваться (`zbarimg` не находил), а `t.me/CodoCeh` — сканировался нормально. Причина — Reed-Solomon блоки с error-correction по-разному раскладываются в зависимости от конкретных закодированных данных, поэтому одна и та же зона перекрытия может задеть критичную часть ОДНОГО конкретного блока сильнее, чем в среднем по всей картинке (общий бюджет error-correction на уровне H — не гарантия, если повреждение сконцентрировано). **Правило: после ЛЮБОГО QR+лого — обязательно декодировать реальным сканером (`brew install zbar`, `zbarimg <file>`), не полагаться на «в теории должно сканироваться».** Рабочий безопасный порог для этого лого — 24% ширины QR (сработало для обоих URL).
- Также лишний повод убедиться: реальный тап на устройстве — обязательный последний шаг ЛЮБОЙ визуальной правки обоев/десктопа, скриншот самого PNG-файла ничего не говорит про то, как XFCE его реально смасштабирует/обрежет под конкретное разрешение экрана.

**НАЙДЕНО И ИСПРАВЛЕНО 2026-08-16 — «рабочий стол живёт недолго и отлетает в Disconnected» (главная причина нестабильности).**
Владелец пожаловался: через несколько минут АКТИВНОГО использования (не простоя!) AVNC уходит в «Disconnected». Причина найдена по логам, не на словах — `adb logcat`, поиск по `phantom`:
```
ActivityManager: Killing PhantomProcessRecord {... ssh-agent/u0a363}: Trimming phantom processes
```
**Это Android 12+ Phantom Process Killer** — система считает КАЖДЫЙ процесс, запущенный внутри Termux (все процессы XFCE-сессии: Xtigervnc, xfce4-session, xfwm4, dbus-daemon×N, ssh-agent, gpg-agent, gvfsd, Thunar и т.д.) как «phantom process» одного приложения (`com.termux`), и жёстко ограничивает их число (дефолт ~32). Базовый набор процессов уже запущенной XFCE-сессии — порядка 20+; при активном использовании (открыл пару окон, файловый менеджер, терминал) счётчик перелетает лимит, и Android НАЧИНАЕТ УБИВАТЬ процессы — если убитым оказался Xtigervnc или что-то из ядра сессии (xfwm4/xfce4-session/dbus), вся сессия падает целиком. Подтверждено эмпирически: `dumpsys activity processes | grep -c PhantomProcessRecord` показывал именно процессы XFCE-сессии; после специально устроенного стресс-теста (наспамил 6 Thunar + 6 xfce4-terminal) в логе тут же появились `Killing PhantomProcessRecord`.
**Фикс (через adb с компьютера, НЕ делается изнутри самого телефона/Termux):**
```
adb shell settings put global settings_enable_monitor_phantom_procs false
adb shell device_config put activity_manager max_phantom_processes 2147483647
```
После фикса — тот же стресс-тест (6 Thunar + 6 терминалов, счётчик процессов до 57) прошёл БЕЗ единого phantom-kill, сессия осталась live, VNC-порт listening, AVNC не отвалился. **Это ключевое отличие от известных ранее версий памяти о нестабильности** — до этого момента причина ошибочно связывалась с Doze/battery-менеджментом (см. пункт про Doze ниже — ОН НЕ ПРИЧИНА, Doze-режим процессы пережили штатно за 5 минут форсированного теста; реальная причина — именно phantom process killer, срабатывающий даже при активном экране).
- **Важная оговорка:** `settings put global` / `device_config put` обычно переживают перезагрузку (хранятся в settings.db), но если баг вернётся — первым делом перепроверить именно эти два значения (`adb shell settings get global settings_enable_monitor_phantom_procs` должно быть `false`, `adb shell device_config get activity_manager max_phantom_processes` — большое число, не `null`) и переприменить fix, а не искать причину заново.
- Фикс требует ADB (компьютер + USB debugging), самостоятельно с телефона не воспроизводится и не чинится.

**Известные грабли при работе с этим окружением:**
- `am start` изнутри Termux (через `run-as`/скрипт) для запуска СТОРОННЕГО приложения (не `com.termux.*`) блокируется Android как «Background activity launch» — код 102 — если Termux не видим на экране в момент вызова (в т.ч. если телефон просто ЗАБЛОКИРОВАН). При реальном тапе пользователя по ярлыку это не мешает — вызывающее приложение в этот момент фактически на переднем плане. Именно поэтому «вернуться на домашний экран» надёжно делает `StopActivity` сама (см. выше), а не скрипт из фона Termux.
- **Проверка через `adb shell am start -n <компонент>` — НЕ то же самое, что реальный тап по иконке, даже когда она BAL-exempt.** В этом окружении оба пути технически запускают одну и ту же Activity, но на практике поведение может РАСХОДИТЬСЯ (задачи/фокус/то, что видит система при резолюции intent'а). Реальный прогон 2026-08-15 вскрыл баг (см. следующий пункт), который серия успешных `adb shell am start` циклов не заметила вообще. **Для итоговой проверки «работает ли одно-таповый запуск» — только настоящий `adb shell input tap <x> <y>` по иконке в реальном app drawer/home screen, никогда не component-name shortcut.**
- **TigerVNC банит собственный IP при неполном чтении RFB-баннера.** Если код читает только часть 12-байтного баннера (`"RFB 003.008\n"`) и сразу закрывает сокет — Java отправляет TCP RST (в буфере приёма ещё остались непрочитанные байты), TigerVNC логирует это как `VNCSConnST: Closing ...: read: Connection reset by peer (104)`, и после нескольких таких «недоручитанных» коннектов подряд — `Connections: Blacklisted: 127.0.0.1`, после чего ЛЮБОЕ следующее подключение (включая настоящее, от AVNC) получает `Connection refused`/сброс. Была попытка «прогреть» первое подключение несколькими доп. коннект/дисконнект циклами перед запуском AVNC — это как раз и есть способ ГАРАНТИРОВАННО себя забанить, а не избежать флакинеса. **Фикс: читать баннер ПОЛНОСТЬЮ (все 12 байт) перед закрытием сокета — тогда закрытие чистое (FIN), никакого RST, никакого бана, и никакие «прогревочные» повторы не нужны вообще.** Диагностика — грепать `~/start-gui.log` на `Blacklist`/`reset by peer`.
- **Собственный `startActivity()` без `FLAG_ACTIVITY_NEW_TASK` затягивает чужую активность в СВОЮ задачу** — если `MainActivity` открывает AVNC без `NEW_TASK`, `VncActivity` встраивается в задачу самого лаунчера; следующий тап по ярлыку лаунчера тогда просто поднимает СТАРЫЙ экран AVNC на передний план (`adb shell am start` печатает «its current task has been brought to the front») и НЕ создаёт новый экземпляр `MainActivity` — `start-gui.sh` тихо перестаёт перезапускаться. Диагностируется через `dumpsys activity activities` (чужая activity как top в задаче с `A=<uid>:<свой-пакет>`). Фикс — `FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP` (не `CLEAR_TASK` — та комбинация стирает сохранённый список серверов AVNC).
- `wmctrl -l` на свежесозданном VNC-дисплее может зависнуть намертво (гонка с ещё не до конца инициализированным xfwm4) — не использовать в скриптах без обёртки `timeout`; вместо него — `xdotool search --name`.
- Прямой перезапуск одного процесса (`xfdesktop`) вне полной сессии `startxfce4` не работает — падает `xfconf-CRITICAL: assertion XFCONF_IS_CHANNEL failed`, т.к. нет доступа к тому же D-Bus session bus. Правки xfconf (обои, тема) требуют ПОЛНОГО `stop-gui.sh`+`start-gui.sh`, точечный рестарт демона не годится — тот же класс проблемы, что и cross-proot D-Bus вообще.
- Обои КодоЦех (`/usr/share/backgrounds/codoceh/`) настроены на RandR-выход `VNC-0` в `xfce4-desktop.xml` — подтверждены fullscreen и корректными на чистом рестарте сессии (2026-08-15); если после правки xfconf вживую обои вдруг слетели на дефолтные — не гоняться за live xfconf-query (ненадёжен через отдельный `proot-distro login`), просто чистый `stop-gui.sh`+`start-gui.sh`.
- Antigravity IDE падает с «terminated unexpectedly (clean-exit, code 0)» сразу после старта — воспроизведено и на Termux:X11, и на VNC одинаково, это не связано с рендером; отдельная нерешённая проблема (ptyHost/extension host/network service падают вместе с рендерером).
- Тестирование через `adb shell run-as com.termux ...` даёт минимальное окружение (без `PATH`/`PREFIX`/`TMPDIR`) — `proot-distro` внутри такого вызова падает с «proot utility does not exist». Всегда явно экспортировать `HOME/PREFIX/LD_LIBRARY_PATH/PATH/TMPDIR` (см. любой из `*.sh` в `~/`), иначе даже корректный скрипт не найдёт `proot`. Многострочные inline-команды через `adb shell run-as ... bash -c '...'` часто ломаются переквотированием adb — надёжнее писать во временный файл, `adb push`, копировать в `$HOME` и запускать по пути.
- `proot-distro login debian --shared-tmp` шарит с хостом ТОЛЬКО `$TMPDIR` (не весь `$HOME`) — нет моста `/host-rootfs`. Чтобы передать файл с Mac внутрь proot: `adb push` → `run-as com.termux cp` в `$TMPDIR` (`/data/data/com.termux/files/usr/tmp`) → внутри proot файл виден по пути `/tmp/<имя>`.

1С-разработка с этого телефона — не локально (ARM64-платформы для Linux не существует), только по SSH на mac mini, см. `~/.gemini/GEMINI.md` §5.1b и [[onec-epf-no-1c-runtime]].

Симлинк/синхронизация сессий Claude Code: карточки (`~/.config/Claude/claude-code-sessions/.../local_*.json`) — разовый снепшот с MacBook (139 шт., 2026-08-14), НЕ живая синхронизация; см. [[claude-code-desktop-sessions-storage]] за механикой хранения самих карточек на любой машине.

**Доступ с MacBook по кабелю (выяснено 2026-08-17, когда Tailscale на телефоне был офлайн).** Рабочий путь — adb с пробросом ssh-порта Termux, а не сеть:
```
adb forward tcp:8022 tcp:8022
ssh -p 8022 u0_a363@127.0.0.1
```
Имя пользователя выводится из UID пакета: `adb shell pm list packages -U | grep termux` даёт `uid:10363` → `u0_a363`. Ключ кладётся в `~/.ssh/authorized_keys` **самого Termux** (`/data/data/com.termux/files/home`), а НЕ внутри proot-Debian — это разные домашние каталоги, и `sshd` Termux читает только свой; типичная ошибка — выполнить команду, уже находясь внутри proot.

**ОБНОВЛЕНО 2026-08-24 — буфер обмена и файлы между Android и Линуксом внутри `ru.codoceh.stol` уже работают из коробки, ничего настраивать не нужно.**
- **Текстовый буфер обмена — двусторонний, встроен в само приложение** (в APK найдены `ClipboardManager`, `lastSentClip`/`lastReceivedClip`, `OnPrimaryClipChangedListener`). Проверено вживую: скопировал строку в терминале «Стола» (тройной тап по строке = выделение = автокопирование в X11, тут же тост «Скопировано») — строка сразу стала подсказкой буфера Android-клавиатуры. И наоборот: скопировал строку в адресной строке Chrome (Ctrl+A/Ctrl+C) — тут же вставилась в терминал «Стола» через клавиатурную подсказку/Ctrl+Shift+V.
- **Файлы — НЕ через буфер, а через готовую расшаренную папку.** В самой команде запуска приложения (реальный `proot`-инвок, вытащен из `/proc/<pid>/cmdline`) уже стоят бинды: `-b /storage/emulated/0/Download:/root/Загрузки -b /storage/emulated/0/Documents:/root/Документы`. Проверено вживую: `touch /root/Загрузки/x` (файл создан ВНУТРИ Линукса) — мгновенно появился в `/sdcard/Download/x` (обычный Android). Обратное направление (файл, положенный в Android Downloads ЧЕРЕЗ ОБЫЧНОЕ приложение — браузер, «Поделиться» и т.п.) должно быть видно в `/root/Загрузки` тем же механизмом; **НЕ проверено для файлов, заброшенных туда сырым `adb push`** — такой файл внутри Линукса НЕ появился (вероятно, Android scoped storage: `adb push` не проходит через MediaStore, а bind-mount читается от имени UID самого приложения, которое видит только то, что видно через обычный API общего хранилища). Для реального использования (сохранил файл через приложение на телефоне) это не помеха.
- **Печатать кириллицу через `adb shell input text` — падает с `NullPointerException`** (у системного `InputShellCommand`, не у самого приложения). Путь `/root/Загрузки` содержит кириллицу «Загрузки» — печатать его нельзя. **Обход:** `cd` с маской из `?` по количеству букв (`cd /root/????????` — 8 знаков на «Загрузки», уникально после того как убрать случайные 8-буквенные тёзки типа `pull.log`), затем работать внутри обычными ASCII-именами. Глоб **не годится** для СОЗДАНИЯ нового пути (`touch /root/????????/new.txt` тоже падает — глоб матчит только существующее), только `cd` в существующую директорию.
- **Прямой SSH с телефона на mac mini по Tailscale не работает — обнаружено при попытке настроить авто-синк памяти/скиллов.** `git clone`/`ssh` до `100.84.177.25:2222` виснут на `kex_exchange_identification: Connection closed by remote host` — сразу после TCP-коннекта (который проходит успешно, `curl` подтверждает), сервер рвёт соединение ДО обмена SSH-баннерами. На mini проверено: не fail2ban/sshguard/hosts.deny/Little Snitch/LuLu, не MaxStartups (0 висящих соединений), не `sshd_config`/`sshd_config.d` — конфиг чист. **Решающий тест:** тот же SSH-клиент с телефона к `github.com` прошёл ПОЛНОСТЬЮ (KEX, алгоритмы, отказ только на подборе ключа) — значит клиент исправен, проблема именно в пути телефон↔mini через Tailscale. Похоже на связку с уже известным по [[hy2_carrier_port_filter]] — оператор режет UDP кроме 443/8443, Tailscale уходит в DERP-релей (подтверждено логом `tailscaled`: `magicsock: home DERP... connGen=1`, не прямой P2P), а релей достаточно нестабилен/задерган, чтобы ломать строгий построчный SSH-хендшейк, хотя простой `curl`/HTTP это переживает. **Добит decisive-тестом (позже в той же сессии): проблема не в порте/протоколе, а в самом канале.**
Поднят голый `python3 -m http.server` на mini на новом порту (без всякого Tailscale Serve/TLS-
обёртки) — запрос с телефона `curl` показал "Connected"+"Request completely sent off", но **в логе
сервера на mini этот запрос вообще не появился** (только собственный локальный запрос с mini).
Значит данные телефон→mini не долетают на уровне TCP-payload для этой пары устройств вообще,
независимо от порта/протокола/обёртки — не лечится правкой конфига на mini. Решение — обойти
Tailscale целиком для этого сценария, см. [[claude_config_sync]] (публичный HTTPS через VPS-edge
codoceh.ru вместо прямого канала телефон↔mini).
- **Рабочая схема авто-синка `CLAUDE.md`/скиллов/памяти, вместо прямого SSH с телефона:** Mac — источник истины. `~/Services/claude-config-sync/push.sh` (launchd `com.cebers.claude-config-sync`, раз в час) кладёт снепшот в git и пушит на bare-репо `~/Backups/claude-config.git` на mini (там свой launchd `com.cebers.claude-config-pull` тянет и раскладывает). Тот же `push.sh` в конце дополнительно зовёт `push_phone.sh` — если `adb get-state` видит подключённый по USB телефон, собирает тот же снепшот в tar.gz (**симлинки скиллов на `~/.agents/skills/...` нужно разворачивать `rsync -aL`, не `-a`** — иначе относительный симлинк типа `research -> ../../.agents/skills/research`, скопированный в другую по глубине директорию, становится битым и валит `tar` при паковке/распаковке), поднимает локальный HTTP-сервер + `adb reverse`, забирает архив через `run-as ru.codoceh.stol curl ... -o .../.claude/_sync_payload.tgz` и распаковывает тем же `run-as` — **без открытия самого приложения и без единого клика по экрану.** Если телефон не подключён — `push_phone.sh` тихо завершается (`adb get-state` падает), синк на mini это не трогает. Ограничение: телефон обновляется только когда физически на USB у этого Mac, не по эфиру (пока не решена загадка SSH выше).

**Куда ставить софт на этой машине.** ssh приводит в Debian-proot, но с `HOME=/data/data/com.termux/files/home`, тогда как реальное рабочее окружение (там лежат `.claude`, `.codex`, `.gemini` и туда смотрят приложения) — это **`/root`**. Рабочий стол и стартует с `export HOME=/root` (видно в `ps`). Ставить и раскладывать конфиги нужно с `HOME=/root`, иначе всё ляжет мимо и «ни одного агента не найдено».

**adb shell не может дёргать Termux.** `am startservice ... com.termux.RUN_COMMAND` из adb отбивается: `Requires permission com.termux.permission.RUN_COMMAND` — это разрешение выдаётся приложениям, оболочка его не получает. Писать в `/data/data/com.termux` adb тоже не даёт (`SecurityException` на пользователя 150).

**Снимок экрана.** `adb exec-out screencap -p > f.png` даёт битый PNG: у складного телефона несколько дисплеев, и screencap подмешивает в поток предупреждение «Multiple displays were found». Рабочий путь — снимать на устройство и забирать файлом: `adb shell screencap -p /sdcard/x.png && adb pull /sdcard/x.png`.

**Лаунчер: почему падал при полностью закрытом Termux (найдено и исправлено 2026-08-17).** `MainActivity`/`StopActivity` звали обычный `startService` — с Android 8 система запрещает поднимать ФОНОВЫЙ сервис у приложения, которое не запущено, отсюда «Not allowed to start service». Пока Termux висел в памяти, вызов проходил, и дефект был невидим. Замена на `startForegroundService` (доступен всегда, `minSdk 26`; `RunCommandService` сам сразу вешает уведомление). Исходники лаунчера: `~/Разработки/CodocehDesktopLauncher`, **репозиторий не под git** — правки живут только в файлах.
