Add Joplin, old Mac, and Fedora WiFi runbooks; simplify JA3 Whisky scripts and hotspot recovery.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ahauimix
2026-06-19 22:26:33 +03:00
parent 1938b7c743
commit 4bc9629a09
381 changed files with 3442 additions and 773 deletions

5
.gitignore vendored
View File

@@ -81,6 +81,9 @@ Desktop.ini
# *.tar.gz # *.tar.gz
# *.rar # *.rar
# --- Local overrides (keep repo-agnostic) --- # --- old_mac local secrets ---
.config/old_mac/
docs/old_mac/.env
docs/old_mac/**/.env
*.local *.local
*.local.* *.local.*

View File

@@ -0,0 +1,38 @@
#!/bin/sh
# Диагностика: Mac не видит телефон по USB (заряжается, но нет data / USB-модема).
# Использование:
# ./docs/apps/anytype/scripts/diagnose-phone-usb-mac.sh
# ./docs/apps/anytype/scripts/diagnose-phone-usb-mac.sh watch # опрос каждые 2 с
set -e
watch="${1:-}"
scan() {
printf '\n=== %s ===\n' "$(date '+%H:%M:%S')"
echo "--- USB (iPhone / Android) ---"
if system_profiler SPUSBDataType 2>/dev/null | grep -qiE 'iPhone|iPad|Apple Mobile|Android|MTP|PTP|Vendor ID: 0x05ac|Vendor ID: 0x18d1|Vendor ID: 0x04e8|Vendor ID: 0x2717'; then
system_profiler SPUSBDataType 2>/dev/null | grep -B1 -A6 -iE 'iPhone|iPad|Apple Mobile|Android|MTP|PTP|Vendor ID: 0x05ac|Vendor ID: 0x18d1|Vendor ID: 0x04e8|Vendor ID: 0x2717'
else
echo "Телефон в USB-дереве НЕ найден (только зарядка или кабель/порт без data)."
fi
echo "--- usbmuxd (iPhone) ---"
pgrep -lf usbmuxd 2>/dev/null || echo "usbmuxd не запущен"
if command -v idevice_id >/dev/null 2>&1; then
echo "idevice_id -l: $(idevice_id -l 2>&1 || true)"
fi
echo "--- Сетевые интерфейсы (ожидаем iPhone USB / RNDIS) ---"
networksetup -listallnetworkservices 2>/dev/null | grep -iE 'iphone|usb|rndis|android' || echo "(службы USB-модема нет)"
echo "--- Совет ---"
echo "1) Кабель с передачей данных (не «только зарядка»)"
echo "2) Порт Mac напрямую, не через хаб (сейчас виден Genesys USB Hub)"
echo "3) iPhone: Разблокировать → «Доверять» → Настройки → Режим модема → USB"
echo "4) Android: USB → «Передача файлов» или «USB-модем»"
}
if [ "$watch" = "watch" ]; then
echo "Следим за USB. Переподключите телефон. Ctrl+C для выхода."
while true; do scan; sleep 2; done
else
scan
fi

View File

@@ -0,0 +1,84 @@
# Joplin — локальный синк Mac ↔ телефон (WebDAV, без Docker)
Синхронизация через **rclone WebDAV** только в локальной сети. Данные на Mac в `~/JoplinSync`. Облака Joplin/Dropbox не используются.
---
## Быстрый старт (Mac)
```bash
brew install rclone # если ещё нет
chmod +x ./docs/apps/joplin/scripts/joplin-webdav-*.sh
./docs/apps/joplin/scripts/joplin-webdav-install.sh
```
Показать URL и статус:
```bash
./docs/apps/joplin/scripts/joplin-webdav-status.sh
```
Учётка: `~/.config/joplin-webdav/env` (права `600`, **не коммитить**).
Остановка / запуск:
```bash
./docs/apps/joplin/scripts/joplin-webdav-stop.sh
./docs/apps/joplin/scripts/joplin-webdav-serve.sh
```
**После смены пароля в `~/.config/joplin-webdav/env`** — обязательно перезапуск (иначе rclone держит старый пароль → **401**):
```bash
./docs/apps/joplin/scripts/joplin-webdav-reload.sh
```
Пароль в Joplin на телефоне и Mac должен **совпадать с `JOPLIN_WEBDAV_PASS` в env**, не со старым из установки.
Автозапуск при входе: LaunchAgent `com.eternal.joplin-webdav` (ставится install-скриптом).
---
## Настройка Joplin (Mac и телефон)
1. **Настройки → Синхронизация**
2. Внизу: **Self-hosting → Click here to select one**
3. **WebDAV**
4. Заполнить:
| Поле | Значение |
|------|----------|
| WebDAV URL | **Mac:** `http://127.0.0.1:8080` · **Телефон:** `http://10.219.227.100:8080` (статический IP на Mac — [STABLE_SETUP](../../connectivity/apps/joplin/STABLE_SETUP.md)) |
| Логин | `joplin` |
| Пароль | из `~/.config/joplin-webdav/env``JOPLIN_WEBDAV_PASS` |
5. **Проверить конфигурацию синхронизации** → OK → синхронизировать.
**Важно:** Mac и телефон в **одной сети** (хотспот телефона). URL с `http://`, не `https://`. Без слэша в конце URL.
Опционально: **Настройки → Шифрование** — E2EE (мастер-пароль), на сервере только шифротекст.
---
## Приватность
- Заметки на телефоне — в песочнице **Joplin**, не «доступ ко всем файлам».
- WebDAV слушает `0.0.0.0:8080` только пока запущен rclone; не открывайте порт в интернет.
- При включённом фаерволе macOS разрешите вход для **rclone** или порта **8080** в локальной сети.
---
## Если не коннектится с телефона
1. `./docs/apps/joplin/scripts/joplin-webdav-status.sh` — процесс и IP.
2. На телефоне в браузере: `http://IP_MAC:8080` (может запросить логин — значит сеть доходит).
3. Синк сломался — [RUNBOOK](../../connectivity/apps/joplin/RUNBOOK.md); стабильная схема без смены URL — [STABLE_SETUP](../../connectivity/apps/joplin/STABLE_SETUP.md).
4. Логи: `~/Library/Logs/joplin-webdav/`
---
## Связанное
- **[Runbook: синк сломался (хотспот, логи, ETIMEDOUT)](../../connectivity/apps/joplin/RUNBOOK.md)**
- [Anytype (P2P, без WebDAV)](../anytype/README.md)
- [diagnose-phone-usb-mac.sh](../anytype/scripts/diagnose-phone-usb-mac.sh) — если понадобится USB

View File

@@ -0,0 +1,31 @@
#!/bin/sh
# Полная настройка Joplin для Android-хотспота (Mac = клиент WiFi).
# .local с телефона не работает → alias *.100 + LaunchAgent.
#
# ./joplin-android-hotspot-setup.sh install
set -e
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
echo "=== Joplin: Android-хотспот (Mac клиент) ==="
echo ""
echo "Почему не eternals.local:"
echo " Android, раздающий хотспот, не резолвит .local имён подключённых устройств."
echo " rclone.log — только 127.0.0.1, запросов с телефона нет."
echo ""
"$ROOT/joplin-hotspot-alias-install.sh"
echo ""
echo "Поднимаем alias (sudo)..."
"$ROOT/joplin-hotspot-alias.sh" ensure
echo ""
"$ROOT/joplin-hotspot-alias.sh" status
echo ""
echo "=== На телефоне в Joplin WebDAV ==="
grep '^JOPLIN_PHONE_URL=' "${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}" 2>/dev/null \
| cut -d= -f2- || phone_url=$("$ROOT/joplin-hotspot-alias.sh" status | awk '/URL на телефоне/{getline; print}' | tr -d ' ')
echo " $(grep '^JOPLIN_PHONE_URL=' "$HOME/.config/joplin-webdav/env" 2>/dev/null | cut -d= -f2-)"
echo ""
echo "При смене подсети (10.219→10.74) URL меняется — alias LaunchAgent поднимет *.100,"
echo "новый URL: ./joplin-hotspot-alias.sh status"

View File

@@ -0,0 +1,25 @@
#!/bin/sh
# Одноразово: passwordless sudo только для joplin-hotspot-alias.sh ensure
set -e
SCRIPT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)/joplin-hotspot-alias.sh"
DEST="/etc/sudoers.d/joplin-hotspot-alias"
USER_NAME="$(whoami)"
[ -x "$SCRIPT" ] || chmod +x "$SCRIPT"
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT
cat >"$TMP" <<EOF
# Joplin WebDAV: alias *.100 на en0 после reconnect хотспота (только этот скрипт)
${USER_NAME} ALL=(ALL) NOPASSWD: ${SCRIPT} ensure
EOF
echo "Будет создан $DEST:"
cat "$TMP"
echo ""
sudo cp "$TMP" "$DEST"
sudo chmod 440 "$DEST"
sudo visudo -c -f "$DEST"
echo "OK. LaunchAgent сможет поднимать alias без пароля."

View File

@@ -0,0 +1,45 @@
#!/bin/sh
# LaunchAgent: каждые 60 с проверять alias *.100 (нужен joplin-hotspot-alias-install-sudo.sh).
set -e
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
ALIAS="$ROOT/joplin-hotspot-alias.sh"
PLIST="$HOME/Library/LaunchAgents/com.eternal.joplin-hotspot-alias.plist"
chmod +x "$ROOT"/joplin-hotspot-alias*.sh
mkdir -p "$HOME/Library/LaunchAgents" "$HOME/Library/Logs/joplin-webdav"
cat >"$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.eternal.joplin-hotspot-alias</string>
<key>ProgramArguments</key>
<array>
<string>$ALIAS</string>
<string>ensure</string>
</array>
<key>StartInterval</key>
<integer>60</integer>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>$HOME/Library/Logs/joplin-webdav/alias-launchd.out.log</string>
<key>StandardErrorPath</key>
<string>$HOME/Library/Logs/joplin-webdav/alias-launchd.err.log</string>
</dict>
</plist>
EOF
launchctl bootout "gui/$(id -u)/com.eternal.joplin-hotspot-alias" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST"
launchctl enable "gui/$(id -u)/com.eternal.joplin-hotspot-alias" 2>/dev/null || true
echo "LaunchAgent com.eternal.joplin-hotspot-alias установлен (каждые 60 с)."
echo "Для работы без пароля один раз:"
echo " $ROOT/joplin-hotspot-alias-install-sudo.sh"
echo "Сейчас:"
"$ALIAS" status

View File

@@ -0,0 +1,149 @@
#!/bin/sh
# Стабильный URL для Joplin на телефоне при Android-хотспоте: http://<подсеть>.100:8080
# Alias *.100 на en0 (DHCP primary не трогаем). LaunchAgent поднимает alias после reconnect.
#
# ./joplin-hotspot-alias.sh status
# ./joplin-hotspot-alias.sh ensure
#
# Passwordless: ./joplin-hotspot-alias-install-sudo.sh
set -e
CONFIG="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}"
WIFI_DEV="${WIFI_DEVICE:-en0}"
PORT="${JOPLIN_WEBDAV_PORT:-8080}"
LOG="$HOME/Library/Logs/joplin-webdav/alias.log"
ipv4_subnet() {
printf '%s\n' "$1" | awk -F. '{print $1"."$2"."$3}'
}
ipv4_last_octet() {
printf '%s\n' "$1" | awk -F. '{print $4}'
}
default_gateway() {
route -n get default 2>/dev/null | awk '/gateway:/{print $2; exit}'
}
load_config() {
if [ -f "$CONFIG" ]; then
# shellcheck disable=SC1090
. "$CONFIG"
fi
PORT="${JOPLIN_WEBDAV_PORT:-$PORT}"
}
joplin_phone_ip() {
load_config
gw="$(default_gateway 2>/dev/null || true)"
[ -n "$gw" ] || return 1
subnet="$(ipv4_subnet "$gw")"
if [ -n "${JOPLIN_STATIC_IP:-}" ]; then
last="$(ipv4_last_octet "$JOPLIN_STATIC_IP")"
else
last=100
fi
printf '%s\n' "${subnet}.${last}"
}
phone_url() {
ip="$(joplin_phone_ip 2>/dev/null || true)"
[ -n "$ip" ] || return 1
printf 'http://%s:%s\n' "$ip" "$PORT"
}
has_ip_on_dev() {
ip="$1"
dev="$2"
ifconfig "$dev" 2>/dev/null | grep -q "inet ${ip} "
}
log_msg() {
mkdir -p "$(dirname "$LOG")"
printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >>"$LOG"
}
run_priv() {
if [ "$(id -u)" -eq 0 ]; then
"$@"
else
sudo "$@"
fi
}
write_phone_url_env() {
url="$(phone_url 2>/dev/null || true)"
[ -n "$url" ] && [ -f "$CONFIG" ] || return 0
if grep -q '^JOPLIN_PHONE_URL=' "$CONFIG" 2>/dev/null; then
sed -i '' "s|^JOPLIN_PHONE_URL=.*|JOPLIN_PHONE_URL=$url|" "$CONFIG" 2>/dev/null \
|| sed -i "s|^JOPLIN_PHONE_URL=.*|JOPLIN_PHONE_URL=$url|" "$CONFIG"
else
printf 'JOPLIN_PHONE_URL=%s\n' "$url" >>"$CONFIG"
fi
}
cmd_status() {
load_config
target="$(joplin_phone_ip 2>/dev/null || true)"
primary="$(ipconfig getifaddr "$WIFI_DEV" 2>/dev/null || true)"
gw="$(default_gateway 2>/dev/null || true)"
url="$(phone_url 2>/dev/null || true)"
echo "mode: Android-хотспот (alias *.100)"
echo "device: $WIFI_DEV"
echo "primary: ${primary:-(none)}"
echo "gateway: ${gw:-(none)}"
echo "joplin IP: ${target:-(no gateway)}"
if [ -n "$target" ] && has_ip_on_dev "$target" "$WIFI_DEV"; then
echo "alias: OK"
elif [ -n "$target" ] && [ "$primary" = "$target" ]; then
echo "alias: OK (primary)"
elif [ -n "$target" ]; then
echo "alias: MISSING — телефон не достучится"
echo " fix: $0 ensure"
fi
if [ -n "$url" ]; then
echo ""
echo "URL на телефоне (работает с Android-хотспота):"
echo " $url"
echo ""
echo "⚠ http://*.local:8080 с телефона НЕ работает (Android не резолвит mDNS клиентов хотспота)"
fi
}
cmd_ensure() {
target="$(joplin_phone_ip 2>/dev/null || true)"
if [ -z "$target" ]; then
log_msg "skip: no gateway"
exit 0
fi
primary="$(ipconfig getifaddr "$WIFI_DEV" 2>/dev/null || true)"
if [ -z "$primary" ]; then
log_msg "skip: no primary IP"
exit 0
fi
if [ "$primary" = "$target" ] || has_ip_on_dev "$target" "$WIFI_DEV"; then
write_phone_url_env
exit 0
fi
log_msg "add alias $target on $WIFI_DEV (primary $primary)"
run_priv ifconfig "$WIFI_DEV" inet "$target" netmask 255.255.255.0 alias
write_phone_url_env
echo "alias OK: $target"
echo "URL на телефоне: $(phone_url)"
}
usage() {
echo "Usage: $(basename "$0") status|ensure" >&2
exit 1
}
case "${1:-ensure}" in
status) cmd_status ;;
ensure) cmd_ensure ;;
-h|--help) usage ;;
*) usage ;;
esac

View File

@@ -0,0 +1,165 @@
#!/bin/sh
# Стабильный URL для Joplin на телефоне: http://<LocalHostName>.local:8080
# Не зависит от подсети хотспота (10.219.x → 10.74.x). Alias *.100 не нужен.
#
# ./joplin-mdns-setup.sh status
# ./joplin-mdns-setup.sh install # записать URL в env (rename опционально)
# ./joplin-mdns-setup.sh verify
set -e
CONFIG="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}"
PORT="${JOPLIN_WEBDAV_PORT:-8080}"
WIFI_DEV="${WIFI_DEVICE:-en0}"
# Пусто = текущий LocalHostName Mac (у вас часто Eternals → eternals.local)
PREFERRED_HOST="${JOPLIN_MDNS_HOST:-}"
load_config() {
if [ -f "$CONFIG" ]; then
# shellcheck disable=SC1090
. "$CONFIG"
fi
PORT="${JOPLIN_WEBDAV_PORT:-$PORT}"
PREFERRED_HOST="${JOPLIN_MDNS_HOST:-$PREFERRED_HOST}"
}
current_local_hostname() {
scutil --get LocalHostName 2>/dev/null || true
}
mdns_host() {
load_config
if [ -n "$PREFERRED_HOST" ]; then
printf '%s\n' "$PREFERRED_HOST"
else
current_local_hostname
fi
}
mdns_fqdn() {
printf '%s.local\n' "$(mdns_host | tr '[:upper:]' '[:lower:]')"
}
phone_url() {
host="$(mdns_host | tr '[:upper:]' '[:lower:]')"
printf 'http://%s.local:%s\n' "$host" "$PORT"
}
primary_ip() {
ipconfig getifaddr "$WIFI_DEV" 2>/dev/null || true
}
resolve_mdns_ip() {
host="$(mdns_fqdn)"
dscacheutil -q host -a name "$host" 2>/dev/null \
| awk '/ip_address:/ {print $2}' | grep -v '^127\.' | head -1
}
cmd_status() {
load_config
local_name="$(current_local_hostname)"
host="$(mdns_host)"
ip="$(primary_ip)"
resolved="$(resolve_mdns_ip 2>/dev/null || true)"
url="$(phone_url)"
echo "LocalHostName: ${local_name:-?}"
echo "mDNS host: $host$(mdns_fqdn)"
echo "primary $WIFI_DEV: ${ip:-(none)}"
echo "mDNS → IP: ${resolved:-(none)}"
echo ""
echo "mDNS URL (НЕ для Android-хотспота — телефон не резолвит .local клиентов):"
echo " $url"
echo "URL на Mac: http://127.0.0.1:${PORT}"
echo "Телефон-хотспот: ./joplin-hotspot-alias.sh status"
if [ -n "$PREFERRED_HOST" ] && [ "$local_name" != "$PREFERRED_HOST" ]; then
echo ""
echo "В env JOPLIN_MDNS_HOST=$PREFERRED_HOST, LocalHostName=$local_name"
echo " install переименует Mac или уберите JOPLIN_MDNS_HOST из env"
elif [ -n "$ip" ] && [ -n "$resolved" ] && [ "$ip" = "$resolved" ]; then
echo ""
echo "OK: mDNS на Mac резолвится (для телефона используйте alias *.100)"
elif [ -n "$ip" ] && [ -z "$resolved" ]; then
echo ""
echo "⚠ mDNS пока не резолвится — подождите или переподключите WiFi"
elif [ -z "$ip" ]; then
echo ""
echo "⚠ Нет IP на $WIFI_DEV"
fi
}
write_env_mdns() {
load_config
url="$(phone_url)"
host="$(mdns_host)"
if [ ! -f "$CONFIG" ]; then
echo "Нет $CONFIG — сначала joplin-webdav-install.sh" >&2
return 1
fi
if grep -q '^JOPLIN_MDNS_HOST=' "$CONFIG" 2>/dev/null; then
sed -i '' "s|^JOPLIN_MDNS_HOST=.*|JOPLIN_MDNS_HOST=$host|" "$CONFIG" 2>/dev/null \
|| sed -i "s|^JOPLIN_MDNS_HOST=.*|JOPLIN_MDNS_HOST=$host|" "$CONFIG"
else
printf 'JOPLIN_MDNS_HOST=%s\n' "$host" >>"$CONFIG"
fi
if grep -q '^JOPLIN_PHONE_URL=' "$CONFIG" 2>/dev/null; then
sed -i '' "s|^JOPLIN_PHONE_URL=.*|JOPLIN_PHONE_URL=$url|" "$CONFIG" 2>/dev/null \
|| sed -i "s|^JOPLIN_PHONE_URL=.*|JOPLIN_PHONE_URL=$url|" "$CONFIG"
else
printf 'JOPLIN_PHONE_URL=%s\n' "$url" >>"$CONFIG"
fi
}
cmd_install() {
load_config
local_name="$(current_local_hostname)"
target="$(mdns_host)"
if [ -n "$PREFERRED_HOST" ] && [ "$local_name" != "$PREFERRED_HOST" ]; then
echo "Переименование LocalHostName: $local_name$PREFERRED_HOST"
if [ "$(id -u)" -eq 0 ]; then
scutil --set LocalHostName "$PREFERRED_HOST"
else
sudo scutil --set LocalHostName "$PREFERRED_HOST"
fi
else
echo "Используем текущий LocalHostName: $local_name (→ $(mdns_fqdn))"
fi
write_env_mdns
echo ""
echo "На телефоне в Joplin WebDAV (один раз):"
echo " $(phone_url)"
echo ""
sleep 2
cmd_verify || true
}
cmd_verify() {
ip="$(primary_ip)"
resolved="$(resolve_mdns_ip 2>/dev/null || true)"
if [ -z "$ip" ]; then
echo "verify: нет IP на $WIFI_DEV" >&2
exit 1
fi
if [ "$ip" = "$resolved" ]; then
echo "verify OK: $(mdns_fqdn)$ip"
exit 0
fi
echo "verify: mDNS=${resolved:-none} ip=$ip (часто OK через 30с после connect)" >&2
exit 1
}
usage() {
echo "Usage: $(basename "$0") status|install|verify" >&2
exit 1
}
case "${1:-status}" in
status) cmd_status ;;
install) cmd_install ;;
verify) cmd_verify ;;
-h|--help) usage ;;
*) usage ;;
esac

View File

@@ -0,0 +1,90 @@
#!/bin/sh
# Установка: конфиг, launchd (автозапуск), первый старт WebDAV.
set -e
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
CONFIG_DIR="$HOME/.config/joplin-webdav"
CONFIG="$CONFIG_DIR/env"
LAUNCH_AGENT="$HOME/Library/LaunchAgents/com.eternal.joplin-webdav.plist"
SERVE="$ROOT/joplin-webdav-serve.sh"
mkdir -p "$CONFIG_DIR" "$HOME/JoplinSync" "$HOME/Library/Logs/joplin-webdav"
if [ ! -f "$CONFIG" ]; then
PASS=$(openssl rand -base64 18 | tr -d '/+=' | head -c 24)
cat > "$CONFIG" <<EOF
# Локальный WebDAV для Joplin. Только LAN. Не коммитить.
JOPLIN_WEBDAV_USER=joplin
JOPLIN_WEBDAV_PASS=$PASS
JOPLIN_WEBDAV_PORT=8080
JOPLIN_WEBDAV_ROOT=$HOME/JoplinSync
# Опционально (legacy alias *.100 — не нужен при mDNS):
# JOPLIN_STATIC_IP=10.219.227.100
# JOPLIN_MDNS_HOST=joplin-mac
# JOPLIN_PHONE_URL=http://joplin-mac.local:8080
EOF
chmod 600 "$CONFIG"
echo "Создан $CONFIG"
else
echo "Конфиг уже есть: $CONFIG"
fi
chmod +x "$ROOT"/joplin-webdav-*.sh
if ! command -v rclone >/dev/null 2>&1; then
echo "Устанавливаю rclone..."
brew install rclone
fi
mkdir -p "$HOME/Library/LaunchAgents"
cat > "$LAUNCH_AGENT" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.eternal.joplin-webdav</string>
<key>ProgramArguments</key>
<array>
<string>$SERVE</string>
<string>--foreground</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>$HOME/Library/Logs/joplin-webdav/launchd.out.log</string>
<key>StandardErrorPath</key>
<string>$HOME/Library/Logs/joplin-webdav/launchd.err.log</string>
</dict>
</plist>
EOF
launchctl bootout "gui/$(id -u)/com.eternal.joplin-webdav" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$LAUNCH_AGENT"
launchctl enable "gui/$(id -u)/com.eternal.joplin-webdav" 2>/dev/null || true
launchctl enable "gui/$(id -u)/com.eternal.joplin-webdav" 2>/dev/null || true
chmod +x "$ROOT"/joplin-mdns-setup.sh 2>/dev/null || true
if [ -x "$ROOT/joplin-mdns-setup.sh" ]; then
echo ""
echo "=== mDNS (стабильный URL на телефоне) ==="
if [ "$(scutil --get LocalHostName 2>/dev/null)" = "joplin-mac" ]; then
"$ROOT/joplin-mdns-setup.sh" status || true
else
echo "LocalHostName ещё не joplin-mac. Один раз:"
echo " $ROOT/joplin-mdns-setup.sh install"
fi
fi
sleep 2
"$ROOT/joplin-webdav-status.sh"
echo ""
echo "Пароль для Joplin:"
grep JOPLIN_WEBDAV_PASS "$CONFIG" | cut -d= -f2-
echo ""
echo "На телефоне (один раз, навсегда): http://joplin-mac.local:8080"
echo "Установка имени: $ROOT/joplin-mdns-setup.sh install"

View File

@@ -0,0 +1,7 @@
#!/bin/sh
# Перечитать ~/.config/joplin-webdav/env и перезапустить WebDAV (после смены пароля).
set -e
uid=$(id -u)
launchctl kickstart -k "gui/${uid}/com.eternal.joplin-webdav"
sleep 2
"$(dirname "$0")/joplin-webdav-status.sh"

View File

@@ -0,0 +1,72 @@
#!/bin/sh
# Локальный WebDAV для синхронизации Joplin (Mac ↔ телефон по LAN).
# Данные: ~/JoplinSync. Учётка: ~/.config/joplin-webdav/env
#
# ./joplin-webdav-serve.sh # фон
# ./joplin-webdav-serve.sh --foreground # передний план (для launchd)
set -e
CONFIG="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}"
LOG_DIR="$HOME/Library/Logs/joplin-webdav"
PID_FILE="$HOME/.config/joplin-webdav/rclone.pid"
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
mkdir -p "$LOG_DIR" "$(dirname "$PID_FILE")"
if [ ! -f "$CONFIG" ]; then
echo "Нет $CONFIG — запустите: $SCRIPT_DIR/joplin-webdav-install.sh" >&2
exit 1
fi
# shellcheck disable=SC1090
. "$CONFIG"
: "${JOPLIN_WEBDAV_USER:?}"
: "${JOPLIN_WEBDAV_PASS:?}"
: "${JOPLIN_WEBDAV_PORT:=8080}"
: "${JOPLIN_WEBDAV_ROOT:=$HOME/JoplinSync}"
mkdir -p "$JOPLIN_WEBDAV_ROOT"
RCLONE="$(command -v rclone || echo /opt/homebrew/bin/rclone)"
if [ ! -x "$RCLONE" ]; then
echo "rclone не найден. Установите: brew install rclone" >&2
exit 1
fi
run_rclone() {
exec "$RCLONE" serve webdav "$JOPLIN_WEBDAV_ROOT" \
--addr "0.0.0.0:${JOPLIN_WEBDAV_PORT}" \
--user "$JOPLIN_WEBDAV_USER" \
--pass "$JOPLIN_WEBDAV_PASS" \
--vfs-cache-mode minimal \
--log-file "$LOG_DIR/rclone.log" \
--log-level INFO
}
case "${1:-}" in
--foreground|-f)
run_rclone
;;
*)
if [ -f "$PID_FILE" ]; then
old_pid=$(cat "$PID_FILE")
if kill -0 "$old_pid" 2>/dev/null; then
echo "Уже запущен (PID $old_pid), порт ${JOPLIN_WEBDAV_PORT}"
exit 0
fi
rm -f "$PID_FILE"
fi
nohup "$0" --foreground >>"$LOG_DIR/stdout.log" 2>&1 &
echo $! >"$PID_FILE"
sleep 1
if kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
echo "WebDAV запущен (PID $(cat "$PID_FILE")), порт ${JOPLIN_WEBDAV_PORT}"
else
echo "Не удалось запустить. См. $LOG_DIR/stdout.log" >&2
rm -f "$PID_FILE"
exit 1
fi
;;
esac

View File

@@ -0,0 +1,73 @@
#!/bin/sh
CONFIG="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}"
PID_FILE="$HOME/.config/joplin-webdav/rclone.pid"
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
echo "=== WebDAV Joplin ==="
if [ -f "$CONFIG" ]; then
# shellcheck disable=SC1090
. "$CONFIG"
echo "Пользователь: ${JOPLIN_WEBDAV_USER:-?}"
echo "Порт: ${JOPLIN_WEBDAV_PORT:-8080}"
echo "Папка: ${JOPLIN_WEBDAV_ROOT:-$HOME/JoplinSync}"
else
echo "Конфиг не найден: $CONFIG"
fi
echo ""
echo "=== Процесс ==="
if pgrep -f "rclone serve webdav" >/dev/null 2>&1; then
pgrep -lf "rclone serve webdav" 2>/dev/null || true
else
echo "Не запущен → ./docs/apps/joplin/scripts/joplin-webdav-serve.sh"
fi
port="${JOPLIN_WEBDAV_PORT:-8080}"
echo ""
echo "=== URL для телефона (Android-хотспот) ==="
if [ -x "$SCRIPT_DIR/joplin-hotspot-alias.sh" ]; then
"$SCRIPT_DIR/joplin-hotspot-alias.sh" status
else
echo "(нет joplin-hotspot-alias.sh)"
fi
echo ""
echo "=== mDNS (.local) — с телефона НЕ работает ==="
if [ -x "$SCRIPT_DIR/joplin-mdns-setup.sh" ]; then
host="$(scutil --get LocalHostName 2>/dev/null | tr '[:upper:]' '[:lower:]' || echo '?')"
echo "http://${host}.local:${port} — только Mac/Windows; Android-хотспот не резолвит"
fi
echo ""
echo "=== IP Mac (DHCP, справочно) ==="
ip=$(ipconfig getifaddr en0 2>/dev/null || true)
[ -n "$ip" ] && echo "primary en0: $ip" || echo "нет IP"
echo ""
echo "=== Порт :8080 ==="
lsof -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || echo "не слушает"
echo ""
echo "=== URL в Joplin (Mac) ==="
settings="$HOME/.config/joplin-desktop/settings.json"
if [ -f "$settings" ]; then
grep -o '"sync\.6\.path": *"[^"]*"' "$settings" 2>/dev/null | sed 's/.*"\(http[^"]*\)".*/\1/' \
| while read -r u; do echo "settings.json: $u"; done
fi
echo ""
echo "=== rclone (запросы с телефона?) ==="
log="$HOME/Library/Logs/joplin-webdav/rclone.log"
if [ -f "$log" ]; then
phone_lines=$(grep -E 'from 10\.|from 192\.168\.' "$log" 2>/dev/null | tail -3)
if [ -n "$phone_lines" ]; then
echo "$phone_lines"
else
echo "нет запросов с LAN (только 127.0.0.1?) — телефон не достучался"
fi
fi
echo ""
echo "=== Учётка ==="
[ -f "$CONFIG" ] && echo "Пароль: $CONFIG → JOPLIN_WEBDAV_PASS"

View File

@@ -0,0 +1,18 @@
#!/bin/sh
PID_FILE="$HOME/.config/joplin-webdav/rclone.pid"
if [ ! -f "$PID_FILE" ]; then
echo "WebDAV не запущен (нет $PID_FILE)"
exit 0
fi
pid=$(cat "$PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
sleep 1
kill -9 "$pid" 2>/dev/null || true
echo "Остановлен (PID $pid)"
else
echo "Процесс $pid уже не работает"
fi
rm -f "$PID_FILE"

View File

@@ -1,111 +1,110 @@
# Emergency: сеть после смены хотспота (macOS) # Emergency: сеть после переподключения хотспота (macOS)
Симптом: **отключили Mac от WiFi / переподключились к другому хотспоту — «интернета нет», помогает только перезагрузка.**
Документ описывает **проверенную на машине причину** в вашей конфигурации, **ручное восстановление без перезагрузки** и **скрипт** из репозитория.
--- ---
## Причина (точная по конфигурации) ## Одна команда (из любой папки)
На macOS в **Системных настройках → Сеть → WiFi → Подробнее → Прокси** часто включён **автоматический прокси (PAC)** с URL вида **`http://localhost:1089/proxy.pac`** (см. [PROXY_GUIDE.md](./PROXY_GUIDE.md) → раздел про системный PAC). ```bash
bash /Users/eternal/code/local_machine/docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
```
Проверка на рабочей машине (2026): **PAC для сервиса WiFi включён (Enabled: Yes)**, при этом **на порту 1089 нет процесса**, который отдаёт `proxy.pac` — соединение к `127.0.0.1:1089` отклоняется. Запросит **пароль sudo**, затем: PAC/SOCKS off → **DHCP**`ifconfig down`**`route -n flush`** → DNS → DHCP → **Joplin alias `*.100`**.
В таком состоянии: Диагностика:
1. Система и часть приложений **сначала** должны получить PAC с localhost; при недоступном URL поведение **нестабильно** (часть трафика «в никуда», таймауты). ```bash
2. При **смене хотспота** меняются шлюз, DNS, часто появляется **captive portal**; страница входа может **не открыться**, пока PAC мешает или DNS закеширован со старой сети. bash /Users/eternal/code/local_machine/docs/connectivity/scripts/macos-hotspot-network-recover.sh status
3. **Перезагрузка** «лечит», потому что заново поднимаются **configd**, **mDNSResponder**, порядок инициализации WiFi и иногда снова стартует локальный PACсервер (Clash / Surge / свой сервис на 1089). ```
Дополнительные усилители сбоя (проверять при `status`):
- **Системный SOCKS** на `127.0.0.1:1080` (Shadowsocks и т.д.) — если включён, а клиент не слушает порт, трафик через системный прокси тоже падает.
- **VPN / туннели** с политикой «весь трафик» — после смены сети маршруты могут остаться битые до отключения VPN.
**SOCKS Telegram (1081) и SSHтуннели сами по себе не отключают WiFi** — они приложенческие. Проблема класса «всё мёртво до ребута» у вас с высокой вероятностью — **системный PAC/SOCKS на localhost без живого сервиса + кэш DNS / состояние WiFi**.
--- ---
## Быстро: один скрипт (рекомендуется) ## RCA (почему старый `recover` не помогал)
Из корня репозитория `local_machine`: ### 1. Ядерный сброс не выполнялся
```bash Скрипт проверял `sudo -n` (passwordless). Без NOPASSWD в sudoers он **пропускал** `route flush` и делал только toggle WiFi — **недостаточно** при ARP `REJECT`.
chmod +x ./docs/connectivity/scripts/macos-hotspot-network-recover.sh
./docs/connectivity/scripts/macos-hotspot-network-recover.sh status Ручная «ядерная кнопка» работала, потому что вы вводили пароль и реально сбрасывали маршруты.
./docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
**Исправлено:** `recover` всегда вызывает `sudo` с запросом пароля.
### 2. Manual IPv4 на WiFi (Joplin) ломал маршрут
`/var/log/wifi.log` при reconnect:
```
IP Address: 10.219.227.100
Default Gateway IP Address: 10.219.227.39
No Router MAC Address
ROUTER_MAC_ADDR = 0x000000000000
``` ```
`recover` делает: **выключает PAC** (те же сервисы, что и `macos-system-pac.sh`), **выключает системный SOCKS на WiFi**, по возможности **сбрасывает DNS** и **кратко перезапускает WiFi** и DHCP. В prefs оставался шлюз `10.219.227.39`, хотспот уже в `10.36.136.x``ping``Host is down`, `curl` мёртв.
Если скрипт пишет, что нет `sudo` без пароля — выполните вручную команды из раздела «Вручную» ниже. **Исправлено:** WiFi **только DHCP**; Joplin — **alias** `<подсеть>.100` на `en0` (не трогает default route). См. [apps/joplin/STABLE_SETUP.md](./apps/joplin/STABLE_SETUP.md).
После появления интернета, если вам снова нужен PAC: ### 3. PAC / SOCKS (вторично)
```bash PAC на `:1089` без сервера усиливает сбои, но **не объясняет** `ping 8.8.8.8 → No route to host`.
./docs/connectivity/scripts/macos-pac-on.sh
```
Только **после** того как процесс на **1089** реально запущен и отдаёт `proxy.pac`.
--- ---
## Вручную (без скрипта) ## Что делает `recover` сейчас
### 1. Отключить автоматический PAC | Шаг | Действие |
|-----|----------|
| 1 | PAC off |
| 2 | SOCKS off |
| 3 | `networksetup -setdhcp Wi-Fi` (снимает Manual) |
| 4 | `sudo ifconfig en0 down` |
| 5 | `sudo route -n flush` |
| 6 | DNS flush + `ifconfig up` + `ipconfig DHCP` |
| 7 | Ждёт gateway (до 30 с) |
| 8 | `ifconfig en0 alias <subnet>.100` для Joplin |
**GUI:** Системные настройки → Сеть → WiFi → Подробнее → Прокси → снять **«Автоматическая настройка прокси (PAC)»**. ---
**CLI** (из репо): ## Ядерная кнопка (если скрипт недоступен)
```bash
./docs/connectivity/scripts/macos-pac-off.sh
./docs/connectivity/scripts/macos-system-pac.sh status
```
### 2. Отключить системный SOCKS (если включали для Shadowsocks)
```bash
networksetup -setsocksfirewallproxystate "Wi-Fi" off
networksetup -getsocksfirewallproxy "Wi-Fi"
```
### 3. Сброс DNS
```bash ```bash
sudo networksetup -setdhcp "Wi-Fi"
sudo ifconfig en0 down
sudo route -n flush
sudo dscacheutil -flushcache sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder sudo killall -HUP mDNSResponder
``` sudo ifconfig en0 up
### 4. Обновить DHCP и «пнуть» WiFi
Узнать интерфейс WiFi (часто `en0`):
```bash
networksetup -listallhardwareports | sed -n '/Hardware Port: Wi-Fi/,/Ethernet Address/p'
```
Дальше (подставьте свой `Device`, например `en0`):
```bash
sudo ipconfig set en0 DHCP sudo ipconfig set en0 DHCP
networksetup -setairportpower en0 off bash /Users/eternal/code/local_machine/docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
sleep 2
networksetup -setairportpower en0 on
``` ```
### 5. Captive portal
Откройте в браузере **`http://captive.apple.com`** или **`http://neverssl.com`** — должна открыться страница входа в сеть отели/кафе.
--- ---
## Профилактика (архитектурно) ## Диагностика
1. **Не держать PAC включённым**, если сервис на **1089** не запущен: сначала поднять Clash/Surge/свой HTTPсервер с PAC, **затем** `macos-pac-on.sh`. ```bash
2. Перед подключением к **чужому хотспоту** с обязательной страницей входа — **временно PAC off** (`macos-pac-off.sh`), залогиниться в сеть, затем при необходимости снова **on**. networksetup -getinfo "Wi-Fi"
3. Разнести роли: **глобальный PAC** — только когда стабильно есть локальный конфиг‑сервер; для «чистого» интернета в дороге — **PAC выключен**, прокси только в приложениях (Telegram SOCKS **1081**, Cursor **10809** и т.д.) — см. [TELEGRAM_GUIDE.md](./TELEGRAM_GUIDE.md), [PROXY_EMERGENCY.md](./PROXY_EMERGENCY.md). ifconfig en0 | grep inet
route -n get default
bash /Users/eternal/code/local_machine/docs/connectivity/scripts/macos-hotspot-network-recover.sh status
./docs/apps/joplin/scripts/joplin-webdav-status.sh
grep -E 'Router MAC|No Router' /var/log/wifi.log | tail -5
```
| Признак | Причина |
|---------|---------|
| Manual + другая подсеть в gateway | Старый Joplin static |
| `No Router MAC` в wifi.log | ARP на шлюз не прошёл |
| `REJECT` в route get gateway | Нужен route flush |
| Primary DHCP OK, нет `*.100` alias | Запустить `recover` |
---
## Профилактика
1. **Не** использовать Manual WiFi для Joplin — только DHCP + alias.
2. После **каждого** reconnect: одна команда `recover` выше.
3. PAC on — только если на **1089** слушает сервер.
4. `exit-via-ru-do.sh`**после** рабочего `curl https://1.1.1.1`.
--- ---
@@ -113,10 +112,6 @@ networksetup -setairportpower en0 on
| Документ | Зачем | | Документ | Зачем |
|----------|--------| |----------|--------|
| [PROXY_GUIDE.md](./PROXY_GUIDE.md) | Системный PAC, порты, скрипты `macos-pac-*.sh` | | [apps/joplin/STABLE_SETUP.md](./apps/joplin/STABLE_SETUP.md) | DHCP + alias |
| [PROXY_EMERGENCY.md](./PROXY_EMERGENCY.md) | SSH, SOCKS 1081, прод | | [PROXY_EMERGENCY.md](./PROXY_EMERGENCY.md) | SSH / SOCKS |
| [README.md](./README.md) | Оглавление `connectivity/` | | [README.md](./README.md) | Оглавление |
---
*Если после `recover` сеть всё ещё мёртвая — проверьте аппаратный тумблер WiFi, другое SSID, и логи: Консоль → `Wi-Fi` / `configd`.*

View File

@@ -2,7 +2,7 @@
Одна страница на случай, когда «всё слетело». Полная схема, безопасность и нюансы `~/.ssh/config`: [PROXY_GUIDE.md](./PROXY_GUIDE.md). Одна страница на случай, когда «всё слетело». Полная схема, безопасность и нюансы `~/.ssh/config`: [PROXY_GUIDE.md](./PROXY_GUIDE.md).
**Сеть / хотспот (интернет есть только после перезагрузки):** чаще всего системный **PAC на localhost:1089** без живого сервера или системный SOCKS на **1080**. Пошагово: **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)**. **Сеть / хотспот (интернет есть только после перезагрузки):** чаще всего **Manual IPv4 на WiFi (Joplin)** при смене подсети Android + битые маршруты; также PAC **1089** / SOCKS **1080** без клиента. Пошагово: **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)**.
--- ---

View File

@@ -14,7 +14,9 @@
| **[BUILD_AAB_FROM_RUSSIA_GUIDE.md](./BUILD_AAB_FROM_RUSSIA_GUIDE.md)** | Сборка AAB/APK из России: Maven proxy на проде + SSH-туннель | | **[BUILD_AAB_FROM_RUSSIA_GUIDE.md](./BUILD_AAB_FROM_RUSSIA_GUIDE.md)** | Сборка AAB/APK из России: Maven proxy на проде + SSH-туннель |
| **[WINDOWS_CURSOR_TUNNEL.md](./WINDOWS_CURSOR_TUNNEL.md)** | Cursor из РФ на **Windows**: туннель через прокси, скрипт .ps1, автозапуск | | **[WINDOWS_CURSOR_TUNNEL.md](./WINDOWS_CURSOR_TUNNEL.md)** | Cursor из РФ на **Windows**: туннель через прокси, скрипт .ps1, автозапуск |
| **[WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md)** | Установка SSH-ключа на Windows (один файл `setup-ssh-key-for-proxy.ps1`, только для туннеля) | | **[WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md)** | Установка SSH-ключа на Windows (один файл `setup-ssh-key-for-proxy.ps1`, только для туннеля) |
| **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)** | Сеть после смены хотспота: причина (PAC localhost), ручной сброс без ребута, `macos-hotspot-network-recover.sh` | | **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)** | Сеть после reconnect хотспота: Joplin Manual IP + смена подсети Android, route flush, PAC; `macos-hotspot-network-recover.sh` |
| **[apps/joplin/STABLE_SETUP.md](./apps/joplin/STABLE_SETUP.md)** | Joplin: стабильный URL на телефоне (статический IP Mac на Android-хотспоте) |
| **[apps/joplin/RUNBOOK.md](./apps/joplin/RUNBOOK.md)** | Joplin Mac ↔ телефон: диагностика, ETIMEDOUT/401, логи |
| **[scripts/](./scripts/)** | Скрипты для туннелей и локальной работы | | **[scripts/](./scripts/)** | Скрипты для туннелей и локальной работы |
| **[launchd/](./launchd/)** | LaunchAgent для macOS: автозапуск SOCKS-туннеля при входе | | **[launchd/](./launchd/)** | LaunchAgent для macOS: автозапуск SOCKS-туннеля при входе |
@@ -28,6 +30,7 @@
- **Загрузка файлов на прод через прокси** — [PROXY_GUIDE.md → Загрузка файлов на прод (scp)](./PROXY_GUIDE.md#-загрузка-файлов-на-прод-через-туннель-scp) - **Загрузка файлов на прод через прокси** — [PROXY_GUIDE.md → Загрузка файлов на прод (scp)](./PROXY_GUIDE.md#-загрузка-файлов-на-прод-через-туннель-scp)
- **Безопасность прокси (nginx, fail2ban)** — [PROXY_GUIDE.md → Безопасность](./PROXY_GUIDE.md#-безопасность), SECURITY.md (если есть в папке) - **Безопасность прокси (nginx, fail2ban)** — [PROXY_GUIDE.md → Безопасность](./PROXY_GUIDE.md#-безопасность), SECURITY.md (если есть в папке)
- **Сборка AAB из РФ** — [BUILD_AAB_FROM_RUSSIA_GUIDE.md](./BUILD_AAB_FROM_RUSSIA_GUIDE.md) - **Сборка AAB из РФ** — [BUILD_AAB_FROM_RUSSIA_GUIDE.md](./BUILD_AAB_FROM_RUSSIA_GUIDE.md)
- **Joplin: синк Mac ↔ телефон** — настройка [apps/joplin/STABLE_SETUP.md](./apps/joplin/STABLE_SETUP.md), поломки [apps/joplin/RUNBOOK.md](./apps/joplin/RUNBOOK.md), `./docs/apps/joplin/scripts/joplin-webdav-status.sh`
--- ---
@@ -36,7 +39,7 @@
| Скрипт | Описание | | Скрипт | Описание |
|--------|----------| |--------|----------|
| **telegram-socks-tunnel.sh** | SOCKS5 на 1081 для Telegram. `TELEGRAM_SOCKS_VIA_DO=1` — только Mac→RU→DO (`telegram-socks-via-do`); без RU — ошибка. См. PROXY_GUIDE. | | **telegram-socks-tunnel.sh** | SOCKS5 на 1081 для Telegram. `TELEGRAM_SOCKS_VIA_DO=1` — только Mac→RU→DO (`telegram-socks-via-do`); без RU — ошибка. См. PROXY_GUIDE. |
| **macos-hotspot-network-recover.sh** | После смены хотспота: `status` / `recover` (PAC off, SOCKS off на WiFi, DNS, DHCP, перезапуск радио). См. HOTSPOT_NETWORK_EMERGENCY.md. | | **macos-hotspot-network-recover.sh** | Reconnect хотспота: `bash …/macos-hotspot-network-recover.sh recover` — DHCP, route flush (sudo), Joplin alias `*.100`. См. HOTSPOT_NETWORK_EMERGENCY.md. |
| **ssh-hunab-prod.sh** | Опционально: сначала `hunab-prod-direct`, иначе `hunab-prod` (в конфиге по умолчанию уже через RU). См. PROXY_GUIDE. | | **ssh-hunab-prod.sh** | Опционально: сначала `hunab-prod-direct`, иначе `hunab-prod` (в конфиге по умолчанию уже через RU). См. PROXY_GUIDE. |
--- ---

View File

@@ -0,0 +1,23 @@
# Joplin (локальный синк Mac ↔ телефон)
WebDAV через rclone на Mac, Android-хотспот.
| Документ | Назначение |
|----------|------------|
| **[STABLE_SETUP.md](./STABLE_SETUP.md)** | Рабочая схема: alias `*.100` |
| **[RUNBOOK.md](./RUNBOOK.md)** | Синк сломался |
| [docs/apps/joplin/scripts/](../../../apps/joplin/scripts/) | Скрипты |
## URL (Android-хотспот)
| | |
|---|---|
| Mac | `http://127.0.0.1:8080` |
| Телефон | `http://<подсеть>.100:8080` (см. `joplin-hotspot-alias.sh status`) |
**Не** `http://*.local:8080`с телефона-хотспота не работает.
```bash
./docs/apps/joplin/scripts/joplin-webdav-status.sh
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh status
```

View File

@@ -0,0 +1,263 @@
# Runbook: Joplin — синк Mac ↔ телефон (WebDAV / Android-хотспот)
Симптом: **синхронизация перестала работать** между Joplin на Mac и на телефоне.
Стек: **rclone serve webdav** на Mac → `~/JoplinSync` → Joplin (Mac + Android) по HTTP. Облако Joplin не используется.
**Рабочая схема (2026-06, проверено):** Mac на **DHCP**, alias **`*.100`** на `en0`, телефон → `http://<подсеть>.100:8080`. Подробно: [STABLE_SETUP.md](./STABLE_SETUP.md).
---
## Архитектура
```
Телефон (Joplin) ──HTTP──► Mac en0 alias *.100:8080 ──► rclone ──► ~/JoplinSync/
Mac (Joplin) ──HTTP──► 127.0.0.1:8080 (тот же rclone)
```
| Компонент | Путь / имя |
|-----------|------------|
| Данные | `~/JoplinSync` |
| Учётка | `~/.config/joplin-webdav/env` (`600`, не в git) |
| WebDAV | LaunchAgent `com.eternal.joplin-webdav` |
| Alias `*.100` | LaunchAgent `com.eternal.joplin-hotspot-alias` (опционально + sudoers) |
| Лог rclone | `~/Library/Logs/joplin-webdav/rclone.log` |
| Лог alias | `~/Library/Logs/joplin-webdav/alias.log` |
| Лог Joplin Mac | `~/.config/joplin-desktop/log.txt` |
| Настройки Mac | `~/.config/joplin-desktop/settings.json``sync.6.path` |
Скрипты: `docs/apps/joplin/scripts/`.
---
## Шаг 0: диагностика
```bash
./docs/apps/joplin/scripts/joplin-webdav-status.sh
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh status
```
| Что смотреть | OK | Проблема |
|--------------|-----|----------|
| rclone / `:8080` | процесс слушает | `joplin-webdav-serve.sh` или `joplin-webdav-install.sh` |
| `alias: OK` | `*.100` на en0 | `joplin-hotspot-alias.sh ensure` (sudo) |
| rclone.log | `from 10.` или `from 192.` | только `127.0.0.1` — телефон не достучался |
| Mac URL | `127.0.0.1:8080` | настроить WebDAV на Mac |
---
## Правильные URL
### Android раздаёт хотспот, Mac — клиент WiFi (основной сценарий)
| Устройство | WebDAV URL |
|------------|------------|
| **Mac** | `http://127.0.0.1:8080` |
| **Телефон** | `http://<подсеть>.100:8080` — сейчас часто `http://10.74.21.100:8080` |
Узнать актуальный URL:
```bash
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh status
# или
grep JOPLIN_PHONE_URL ~/.config/joplin-webdav/env
```
**Не использовать на телефоне:**
| URL | Почему |
|-----|--------|
| `http://eternals.local:8080` | Android-хотспот **не резолвит** `.local` клиентов; в rclone.log нет запросов с телефона |
| `http://10.219.227.100:8080` | старая подсеть, если хотспот уже выдал `10.74.x` |
| `http://10.x.x.84:8080` (primary DHCP) | IP меняется при reconnect |
### Общее для Joplin
- Логин: `joplin`
- Пароль: `JOPLIN_WEBDAV_PASS` из env
- **Без** `/` в конце URL
- **Без пробела** в конце
- `http://`, не `https://`
- **Настройки → Синхронизация → Self-hosting → WebDAV** (не «Прокси»)
После смены URL на Mac: **Quit Joplin** → открыть снова → **Синхронизировать**.
---
## Симптом → причина → действие
### 1. «Синк OK, заметок нет на Mac»
Mac Joplin не на WebDAV — в `~/JoplinSync` только данные с телефона.
→ URL `http://127.0.0.1:8080`, проверить конфигурацию, синк.
---
### 2. `ETIMEDOUT` / `connect ETIMEDOUT 10.x.x.x:8080`
**Причина:** на телефоне **устаревший IP** (другая подсеть или нет alias `.100`).
**Признаки:** Joplin `ETIMEDOUT`; rclone — нет свежих `GET`/`PUT` с `10.`.
**Действие:**
```bash
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh ensure # sudo
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh status # новый URL
```
→ Обновить URL на телефоне **только если сменилась подсеть** (`10.219``10.74`).
---
### 3. `401 Unauthorized`
Пароль в Joplin ≠ env, или rclone не перезапущен.
```bash
grep JOPLIN_WEBDAV_PASS ~/.config/joplin-webdav/env
./docs/apps/joplin/scripts/joplin-webdav-reload.sh
```
---
### 4. `Not a valid URL: …/info.json`
Пробел в конце URL в настройках Joplin → переписать URL.
---
### 5. Порт 8080 не слушает
```bash
./docs/apps/joplin/scripts/joplin-webdav-status.sh
./docs/apps/joplin/scripts/joplin-webdav-serve.sh
```
Лог: `~/Library/Logs/joplin-webdav/launchd.err.log`.
---
### 6. `ECONNREFUSED` / телефон «не достаёт», Mac — да
**Причина:** alias **`*.100` отсутствует** на `en0` (слетел после reconnect).
```bash
ifconfig en0 | grep inet
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh status
curl -s -o /dev/null -w '%{http_code}\n' http://10.74.21.100:8080/ # подставьте *.100 из status
```
`joplin-hotspot-alias.sh ensure`
В браузере **на телефоне**: `http://<subnet>.100:8080` — запрос логина
→ Нет интернета на Mac: [HOTSPOT_NETWORK_EMERGENCY.md](../../HOTSPOT_NETWORK_EMERGENCY.md)
---
### 7. `eternals.local` / `*.local` не работает с телефона
**Ожидаемо.** mDNS на Mac резолвится, Android-хотспот — нет. Использовать **`http://<subnet>.100:8080`**, не `.local`.
---
## Процедура: переподключили хотспот / сменили WiFi
### 1. Интернет на Mac
Если `curl` / браузер мёртвы:
```bash
bash /Users/eternal/code/local_machine/docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
```
WiFi должен быть **DHCP**, не Manual ([HOTSPOT_NETWORK_EMERGENCY.md](../../HOTSPOT_NETWORK_EMERGENCY.md)).
### 2. Joplin
```bash
./docs/apps/joplin/scripts/joplin-webdav-status.sh
./docs/apps/joplin/scripts/joplin-hotspot-alias.sh ensure # если alias MISSING
```
Чеклист:
- [ ] rclone слушает `:8080`
- [ ] `alias: OK` (или `ensure` выполнен)
- [ ] **Mac:** `http://127.0.0.1:8080` — без изменений
- [ ] **Телефон:** URL из `status`**не менять**, если подсеть та же (`10.74.21.x`)
- [ ] Подсеть **сменилась** → один раз новый `http://<new-subnet>.100:8080`
- [ ] В rclone.log появились `from 10.` после синка на телефоне
### 3. Авто-alias (рекомендуется один раз)
```bash
bash ./docs/apps/joplin/scripts/joplin-android-hotspot-setup.sh install
bash ./docs/apps/joplin/scripts/joplin-hotspot-alias-install-sudo.sh
```
LaunchAgent поднимает `*.100` каждые 60 с после reconnect.
---
## Скрипты
| Скрипт | Когда |
|--------|--------|
| `joplin-webdav-status.sh` | Первая диагностика |
| `joplin-hotspot-alias.sh status\|ensure` | URL телефона, поднять alias |
| `joplin-android-hotspot-setup.sh install` | Полная настройка хотспота |
| `joplin-hotspot-alias-install-sudo.sh` | Alias без пароля (LaunchAgent) |
| `joplin-webdav-install.sh` | Первая установка rclone + launchd |
| `joplin-webdav-reload.sh` | После смены пароля в env |
| `joplin-mdns-setup.sh` | Справочно; **не для URL на телефоне-хотспоте** |
Путь: `docs/apps/joplin/scripts/`.
---
## Логи
**rclone.log** — успех: `PROPFIND`, `GET`, `PUT from <IP>`. Телефон часто виден как IP **шлюза** (`10.74.21.61`), не IP Mac — нормально для Android.
**log.txt Mac:**
```bash
grep -E 'Synchronizer: \[error\]|ETIMEDOUT|ECONNREFUSED|Unauthorized|Not a valid URL' \
~/.config/joplin-desktop/log.txt | tail -20
```
---
## Безопасность
- WebDAV только в LAN (`0.0.0.0:8080` пока rclone работает); не пробрасывать в интернет.
- env и пароль — не в git.
---
## Связанные документы
| Документ | Зачем |
|----------|--------|
| [STABLE_SETUP.md](./STABLE_SETUP.md) | Схема alias `*.100`, установка |
| [README.md](./README.md) | Оглавление |
| [docs/apps/joplin/README.md](../../../apps/joplin/README.md) | Установка с нуля |
| [HOTSPOT_NETWORK_EMERGENCY.md](../../HOTSPOT_NETWORK_EMERGENCY.md) | Сеть macOS после reconnect |
---
## История инцидентов
| Дата | Симптом | Корень | Фикс |
|------|---------|--------|------|
| 2026-06-02 | 401 | Пароль env ≠ Joplin | `joplin-webdav-reload.sh` |
| 2026-06-08 | ETIMEDOUT | Старый IP на телефоне | Обновить URL |
| 2026-06-09 | Телефон не достаёт `.100` | Mac без alias | `joplin-hotspot-alias ensure` |
| 2026-06-15 | Нет интернета после reconnect | Manual WiFi + битые маршруты | `macos-hotspot-network-recover.sh recover`, DHCP |
| 2026-06-19 | `eternals.local` не работает | Android-хотспот не резолвит mDNS клиентов | URL `http://10.74.21.100:8080` + alias |
| 2026-06-19 | Синк OK | alias `.100` + LaunchAgent | [STABLE_SETUP.md](./STABLE_SETUP.md) |
---
*Актуальный URL телефона: `./docs/apps/joplin/scripts/joplin-hotspot-alias.sh status`.*

View File

@@ -0,0 +1,55 @@
# Стабильная настройка: Android-хотспот + Mac + Joplin
---
## Важно: `.local` с телефона **не работает**
`http://eternals.local:8080` резолвится на **Mac**, но **Android, раздающий хотспот, не умеет** резолвить `.local` имён **подключённых** клиентов (Mac). Запросы до rclone не доходят.
**Рабочая схема:** alias **`*.100`** на `en0` + URL `http://<подсеть>.100:8080`.
| Устройство | URL |
|------------|-----|
| Mac | `http://127.0.0.1:8080` |
| Телефон | `http://10.74.21.100:8080` (подсеть меняется → см. ниже) |
---
## Установка (один раз)
```bash
./docs/apps/joplin/scripts/joplin-webdav-install.sh
bash ./docs/apps/joplin/scripts/joplin-android-hotspot-setup.sh install
```
На телефоне — URL из вывода (`joplin-hotspot-alias.sh status`).
Автоподнятие alias после reconnect:
```bash
./docs/apps/joplin/scripts/joplin-hotspot-alias-install-sudo.sh # sudoers, без пароля
```
---
## После reconnect
1. Alias поднимается LaunchAgent (если sudoers установлен)
2. Проверка: `./docs/apps/joplin/scripts/joplin-webdav-status.sh`
3. Если подсеть **та же** (`10.74.21.x`) — URL на телефоне **не менять**
4. Если подсеть **сменилась** (`10.219``10.74`) — **один раз** новый URL из `status`
Интернет пропал: `bash .../macos-hotspot-network-recover.sh recover`
---
## Почему URL иногда меняется
Android выдаёт **разные подсети** (`10.219.227.x`, `10.74.21.x`). Без Tailscale/VPN **нельзя** иметь один HTTP-URL при любой подсети с телефона-хотспота. Фиксированный **хвост `.100`** + автоподнятие alias минимизируют ручную работу.
---
## Связанное
- [RUNBOOK.md](./RUNBOOK.md)
- [HOTSPOT_NETWORK_EMERGENCY.md](../../HOTSPOT_NETWORK_EMERGENCY.md)

View File

@@ -1,32 +1,244 @@
#!/bin/sh #!/bin/sh
# Сброс сетевого стека macOS после смены хотспота / «интернет не поднимается без перезагрузки». # Сброс сети после переподключения Android-хотспота (macOS).
# Типичная причина в связке с docs/connectivity: системный PAC на localhost:1089 при мёртвом/ещё не
# поднятом PAC-сервере + кэш DNS / WiFi. См. HOTSPOT_NETWORK_EMERGENCY.md
# #
# Использование (из корня репозитория): # RCA (почему старый recover не помогал):
# ./docs/connectivity/scripts/macos-hotspot-network-recover.sh status # - Ядерный сброс (route flush, ifconfig down) выполнялся только при sudo -n (passwordless).
# ./docs/connectivity/scripts/macos-hotspot-network-recover.sh recover # - Без NOPASSWD в sudoers скрипт делал лишь toggle WiFi — ARP REJECT и битые маршруты оставались.
# - Manual IPv4 на WiFi (Joplin 10.219.227.100) конфликтует с новой подсетью хотспота (10.36.x).
#
# Решение:
# - WiFi всегда DHCP (правильный шлюз после reconnect).
# - Joplin: вторичный IP (alias) *.100 в текущей подсети — не трогает default route.
# - recover всегда запрашивает sudo (пароль) для полного сброса.
#
# Из любой папки (одна команда, чинит интернет + Joplin alias):
# bash /Users/eternal/code/local_machine/docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
# #
# Переменные: # Переменные:
# MACOS_PAC_SERVICES — как в macos-system-pac.sh (по умолчанию Wi-Fi|Thunderbolt Bridge 2) # HOTSPOT_JOPLIN_MODE=alias|manual|off alias (по умолчанию при ~/.config/joplin-webdav/env), manual, off
# WIFI_DEVICE например en0; иначе авто: первый Device у Hardware Port: Wi-Fi # HOTSPOT_RECOVER_SKIP_JOPLIN=1 не трогать Joplin alias
# JOPLIN_STATIC_IP последний октет .100 из env; подсеть — текущая
# MACOS_PAC_SERVICES, WIFI_DEVICE
set -e
ROOT="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$(dirname "$0")" && pwd)"
PAC_SCRIPT="$ROOT/macos-system-pac.sh" PAC_SCRIPT="$ROOT/macos-system-pac.sh"
JOPLIN_ENV="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}"
JOPLIN_ALIAS_SCRIPT="${JOPLIN_ALIAS_SCRIPT:-$(cd "$ROOT/../../apps/joplin/scripts" 2>/dev/null && pwd)/joplin-hotspot-alias.sh}"
SCRIPT_SELF="$ROOT/$(basename "$0")"
wifi_device() { wifi_device() {
if [ -n "${WIFI_DEVICE:-}" ]; then if [ -n "${WIFI_DEVICE:-}" ]; then
printf '%s\n' "$WIFI_DEVICE" printf '%s\n' "$WIFI_DEVICE"
return return
fi fi
# "Device: en0" под блоком "Hardware Port: Wi-Fi"
networksetup -listallhardwareports 2>/dev/null \ networksetup -listallhardwareports 2>/dev/null \
| awk '/Hardware Port: Wi-Fi$/ {p=1; next} p && /^Device: / {print $2; exit}' | awk '/Hardware Port: Wi-Fi$/ {p=1; next} p && /^Device: / {print $2; exit}'
} }
ipv4_subnet() {
printf '%s\n' "$1" | awk -F. '{print $1"."$2"."$3}'
}
ipv4_last_octet() {
printf '%s\n' "$1" | awk -F. '{print $4}'
}
load_joplin_env() {
if [ -f "$JOPLIN_ENV" ]; then
# shellcheck disable=SC1090
. "$JOPLIN_ENV"
fi
}
wifi_config_method() {
networksetup -getinfo "Wi-Fi" 2>/dev/null | awk 'NR==1 {print $1; exit}'
}
wifi_manual_router() {
networksetup -getinfo "Wi-Fi" 2>/dev/null | awk -F': ' '/^Router: / {print $2; exit}'
}
wifi_manual_ip() {
networksetup -getinfo "Wi-Fi" 2>/dev/null | awk -F': ' '/^IP address: / {print $2; exit}'
}
default_gateway() {
route -n get default 2>/dev/null | awk '/gateway:/{print $2; exit}'
}
joplin_mode() {
if [ "${HOTSPOT_RECOVER_SKIP_JOPLIN:-0}" = "1" ]; then
echo off
return
fi
if [ -n "${HOTSPOT_JOPLIN_MODE:-}" ]; then
echo "$HOTSPOT_JOPLIN_MODE"
return
fi
# mDNS (joplin-mac.local) — URL на телефоне не зависит от подсети; alias не нужен
echo off
}
joplin_target_ip() {
load_joplin_env
gw="$(default_gateway 2>/dev/null || true)"
[ -n "$gw" ] || return 1
subnet="$(ipv4_subnet "$gw")"
if [ -n "${JOPLIN_STATIC_IP:-}" ]; then
last="$(ipv4_last_octet "$JOPLIN_STATIC_IP")"
else
last=100
fi
printf '%s\n' "${subnet}.${last}"
}
list_en0_alias_ips() {
dev="${1:-en0}"
ifconfig "$dev" 2>/dev/null | awk '/inet / && /alias/ {print $2}'
}
sudo_run() {
echo " \$ sudo $*"
sudo "$@"
}
ensure_dhcp_wifi() {
method="$(wifi_config_method 2>/dev/null || true)"
if [ "$method" = "Manual" ]; then
echo " WiFi Manual $(wifi_manual_ip)$(wifi_manual_router) — переводим на DHCP..."
networksetup -setdhcp "Wi-Fi" 2>/dev/null || sudo_run networksetup -setdhcp "Wi-Fi"
else
echo " WiFi уже DHCP (или не Manual)."
fi
}
nuclear_network_reset() {
dev="$1"
echo " Ядерный сброс на $dev (нужен пароль sudo)..."
sudo_run ifconfig "$dev" down
sudo_run route -n flush
sudo_run dscacheutil -flushcache
sudo_run killall -HUP mDNSResponder
sudo_run ifconfig "$dev" up
sudo_run ipconfig set "$dev" DHCP
}
wait_for_connectivity() {
dev="$1"
i=0
while [ "$i" -lt 30 ]; do
gw="$(default_gateway 2>/dev/null || true)"
ip="$(ipconfig getifaddr "$dev" 2>/dev/null || true)"
if [ -n "$gw" ] && [ -n "$ip" ]; then
if ping -c 1 -W 1500 "$gw" >/dev/null 2>&1; then
echo " Связность OK: IP=$ip gateway=$gw (${i}s)"
return 0
fi
fi
sleep 1
i=$((i + 1))
done
echo " WARN: gateway/IP не поднялись за 30с — проверьте captive portal" >&2
return 1
}
remove_joplin_aliases() {
dev="$1"
target="$2"
for ip in $(list_en0_alias_ips "$dev"); do
case "$ip" in
*.*.*.100|"$target")
echo " Удаляем alias $ip на $dev"
sudo_run ifconfig "$dev" inet "$ip" -alias 2>/dev/null || true
;;
esac
done
}
setup_joplin_alias() {
dev="$1"
target="$(joplin_target_ip 2>/dev/null || true)"
[ -n "$target" ] || { echo " Joplin alias: нет gateway" >&2; return 1; }
primary="$(ipconfig getifaddr "$dev" 2>/dev/null || true)"
if [ "$primary" = "$target" ]; then
echo " Joplin: primary IP уже $target"
return 0
fi
remove_joplin_aliases "$dev" "$target"
if ifconfig "$dev" 2>/dev/null | grep -q "inet $target "; then
echo " Joplin alias $target уже на $dev"
return 0
fi
echo " Joplin alias: $target/24 на $dev (primary DHCP: ${primary:-?})"
sudo_run ifconfig "$dev" inet "$target" netmask 255.255.255.0 alias
echo " URL на телефоне: http://${target}:8080"
if [ -f "$JOPLIN_ENV" ] && [ -n "${JOPLIN_STATIC_IP:-}" ]; then
cur_sub="$(ipv4_subnet "$target")"
env_sub="$(ipv4_subnet "$JOPLIN_STATIC_IP")"
if [ "$cur_sub" != "$env_sub" ]; then
echo " ⚠ Подсеть сменилась ($env_sub.x → $cur_sub.x) — обновите URL в Joplin на телефоне"
fi
fi
}
setup_joplin_manual() {
gw="$(default_gateway 2>/dev/null || true)"
[ -n "$gw" ] || return 1
target="$(joplin_target_ip 2>/dev/null || true)"
echo " Joplin manual (legacy): $target → router $gw"
networksetup -setmanual "Wi-Fi" "$target" 255.255.255.0 "$gw"
}
print_manual_ipv4_status() {
info="$(networksetup -getinfo "Wi-Fi" 2>/dev/null || true)"
echo "$info" | head -5
method="$(printf '%s\n' "$info" | awk 'NR==1 {print $1; exit}')"
dev="$(wifi_device 2>/dev/null || echo en0)"
current_ip="$(ipconfig getifaddr "$dev" 2>/dev/null || true)"
gw="$(default_gateway 2>/dev/null || true)"
echo "--- runtime $dev ---"
echo "primary IP: ${current_ip:-(none)}"
echo "default gateway: ${gw:-(none)}"
aliases="$(list_en0_alias_ips "$dev" | tr '\n' ' ')"
if [ -n "$aliases" ]; then
echo "aliases: $aliases"
fi
if [ -f "$JOPLIN_ENV" ]; then
target="$(joplin_target_ip 2>/dev/null || true)"
echo "joplin target: ${target:-?} (mode: $(joplin_mode))"
if [ -n "$target" ] && ! ifconfig "$dev" 2>/dev/null | grep -q "inet $target "; then
echo "⚠ Joplin IP $target не на интерфейсе — телефон не достучится"
fi
fi
if [ "$method" = "Manual" ]; then
manual_ip="$(printf '%s\n' "$info" | awk -F': ' '/^IP address: / {print $2; exit}')"
manual_router="$(printf '%s\n' "$info" | awk -F': ' '/^Router: / {print $2; exit}')"
if [ -n "$manual_router" ] && [ -n "$gw" ]; then
if [ "$(ipv4_subnet "$manual_router")" != "$(ipv4_subnet "$gw")" ]; then
echo "⚠ MISMATCH: Manual router $(ipv4_subnet "$manual_router").x vs gateway $(ipv4_subnet "$gw").x"
echo " → запустите: bash $SCRIPT_SELF recover"
elif [ -n "$current_ip" ] && [ "$current_ip" != "$manual_ip" ]; then
echo "⚠ MISMATCH: Manual IP $manual_ip, runtime $current_ip"
fi
fi
fi
if [ -n "$gw" ]; then
echo "--- gateway $gw ---"
arp -n "$gw" 2>/dev/null || true
route -n get "$gw" 2>/dev/null | grep -E 'interface|flags' || true
fi
}
usage() { usage() {
printf '%s\n' "Usage: $(basename "$0") status|recover" >&2 cat <<EOF
Usage: $(basename "$0") status|recover
recover — PAC/SOCKS off, DHCP, ядерный сброс (sudo), Joplin alias *.100
status — диагностика
Полная команда из любой папки:
bash $SCRIPT_SELF recover
EOF
exit 1 exit 1
} }
@@ -35,69 +247,70 @@ cmd=$1
case "$cmd" in case "$cmd" in
status) status)
echo "=== Wi-Fi device (hardware) ===" echo "=== Wi-Fi device ==="
wifi_device || true wifi_device || true
echo "=== networksetup -listallnetworkservices (excerpt) ===" echo "=== IPv4 ==="
networksetup -listallnetworkservices 2>/dev/null | head -15 || true print_manual_ipv4_status || true
echo "=== PAC / autoproxy (Wi-Fi + Thunderbolt Bridge 2 if present) ===" echo "=== PAC ==="
if [ -x "$PAC_SCRIPT" ]; then if [ -x "$PAC_SCRIPT" ]; then
MACOS_PAC_SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}" "$PAC_SCRIPT" status || true MACOS_PAC_SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}" "$PAC_SCRIPT" status || true
fi fi
echo "=== SOCKS firewall (Wi-Fi) ===" echo "=== SOCKS (Wi-Fi) ==="
networksetup -getsocksfirewallproxy "Wi-Fi" 2>/dev/null || true networksetup -getsocksfirewallproxy "Wi-Fi" 2>/dev/null || true
echo "=== Web proxy (Wi-Fi) ===" echo "=== PAC listener :1089 ==="
networksetup -getwebproxy "Wi-Fi" 2>/dev/null || true lsof -i :1089 -sTCP:LISTEN 2>/dev/null | head -3 || echo "(none)"
echo "=== Listener on 1089 (PAC URL default) ==="
if lsof -i :1089 -sTCP:LISTEN 2>/dev/null | head -5; then
:
else
echo "(none — при Enabled PAC в Safari/системе возможны сбои)"
fi
echo "=== Default route ===" echo "=== Default route ==="
route -n get default 2>/dev/null | head -12 || true route -n get default 2>/dev/null | head -8 || echo "(no default route)"
echo "=== Quick reachability (ICMP may be blocked on some hotspots) ===" gw="$(default_gateway 2>/dev/null || true)"
ping -c 1 -W 2000 1.1.1.1 >/dev/null 2>&1 && echo "ping 1.1.1.1: ok" || echo "ping 1.1.1.1: fail" if [ -n "$gw" ]; then
ping -c 1 -W 2000 "$gw" >/dev/null 2>&1 && echo "ping gateway: ok" || echo "ping gateway: fail"
fi
curl -sS --max-time 5 -o /dev/null https://1.1.1.1 2>/dev/null && echo "curl: ok" || echo "curl: fail"
;; ;;
recover) recover)
echo "1) Отключаем автоматический PAC на выбранных сервисах..." echo "=== Hotspot recover (DHCP + route flush) ==="
echo "Скрипт запросит пароль sudo для сброса маршрутов."
echo ""
echo "1) PAC off..."
if [ -x "$PAC_SCRIPT" ]; then if [ -x "$PAC_SCRIPT" ]; then
MACOS_PAC_SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}" "$PAC_SCRIPT" off MACOS_PAC_SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}" "$PAC_SCRIPT" off
else
echo "WARN: нет $PAC_SCRIPT — вручную: Системные настройки → Сеть → WiFi → Подробнее → Прокси → снять «Автоматическая настройка прокси»" >&2
fi fi
echo "2) Отключаем системный SOCKS-прокси на Wi-Fi (если был включён Shadowsocks и т.п.)..." echo "2) SOCKS off..."
if networksetup -listallnetworkservices 2>/dev/null | grep -Fqx "Wi-Fi"; then
networksetup -setsocksfirewallproxystate "Wi-Fi" off 2>/dev/null || true networksetup -setsocksfirewallproxystate "Wi-Fi" off 2>/dev/null || true
fi echo "3) WiFi → DHCP (снимаем конфликтный Manual Joplin)..."
echo "3) Сброс DNS-кэша (нужен sudo)..." ensure_dhcp_wifi
if sudo -n true 2>/dev/null; then
sudo dscacheutil -flushcache 2>/dev/null || true
sudo killall -HUP mDNSResponder 2>/dev/null || true
echo " mDNSResponder перезагружен."
else
echo " Пропуск (нет sudo без пароля). Выполните вручную:"
echo " sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder"
fi
dev="$(wifi_device)" dev="$(wifi_device)"
if [ -n "$dev" ]; then if [ -z "$dev" ]; then
echo "4) Обновление DHCP на $dev (нужен sudo)..." echo "Ошибка: WiFi device не найден" >&2
if sudo -n true 2>/dev/null; then exit 1
sudo ipconfig set "$dev" DHCP 2>/dev/null || true
else
echo " Пропуск. Вручную: sudo ipconfig set $dev DHCP"
fi fi
echo "5) Краткое выключение/включение WiFi радиомодуля ($dev)..." echo "4) Ядерный сброс $dev..."
networksetup -setairportpower "$dev" off 2>/dev/null || true nuclear_network_reset "$dev"
sleep 2 echo "5) Ожидание DHCP..."
networksetup -setairportpower "$dev" on 2>/dev/null || true wait_for_connectivity "$dev" || true
mode="$(joplin_mode)"
case "$mode" in
alias)
echo "6) Joplin: alias IP (не ломает маршрутизацию)..."
if [ -x "$JOPLIN_ALIAS_SCRIPT" ]; then
"$JOPLIN_ALIAS_SCRIPT" ensure || true
else else
echo "45) Не удалось определить WiFi device — пропуск DHCP/airport power. Вручную выключите/включите WiFi в меню." setup_joplin_alias "$dev" || true
fi fi
echo "=== Готово. Проверка: ===" ;;
"$0" status manual)
echo "6) Joplin: manual WiFi (legacy)..."
setup_joplin_manual || true
;;
*)
echo "6) Joplin: пропуск (нет $JOPLIN_ENV)"
;;
esac
echo "" echo ""
echo "Если интернет всё ещё нет: откройте хотспот в браузере (captive portal), затем при необходимости снова включите PAC:" echo "=== Итог ==="
echo " ./docs/connectivity/scripts/macos-pac-on.sh" "$SCRIPT_SELF" status
echo ""
echo "Captive portal: http://neverssl.com"
;; ;;
*) *)
usage usage

View File

@@ -166,7 +166,7 @@ find "$HOME/Library/Logs" -type f -size 0 -delete 2>/dev/null
## 🔄 Регулярность очистки ## 🔄 Регулярность очистки
- **Еженедельно:** build-артефакты, пустые логи. - **Еженедельно:** build-артефакты, пустые логи.
- **Ежемесячно:** кэши разработки (npm, node-gyp, TypeScript, pip, .yarn), Library/Caches (Google, Firefox, ShipIt, anytype-updater, ms-playwright, responsivelyapp-updater), Cursor WebStorage/Partitions/projects, уровень 1 из cleanup-disk-now.sh. - **Ежемесячно:** кэши разработки (npm, node-gyp, TypeScript, pip, .yarn), Library/Caches (Google, Firefox, ShipIt, anytype-updater, ms-playwright), Cursor WebStorage/Partitions/projects.
- **При заполнении диска >80%:** всё из ежемесячного + Colima (если не нужен), state.vscdb.backup, приложения (Notion, zoom, LibreOffice, Movavi, Ledger), записи экрана. - **При заполнении диска >80%:** всё из ежемесячного + Colima (если не нужен), state.vscdb.backup, приложения (Notion, zoom, LibreOffice, Movavi, Ledger), записи экрана.
--- ---

View File

@@ -37,51 +37,14 @@ cleanup_dir "$HOME/.yarn" ".yarn кэш"
cleanup_dir "$HOME/Library/Caches/ms-playwright" "Playwright кэш (браузеры для тестов)" cleanup_dir "$HOME/Library/Caches/ms-playwright" "Playwright кэш (браузеры для тестов)"
echo "" echo ""
# 2. Windsurf полностью (кэш + данные; приложение — удалить вручную из Программы) # 2. Кэши браузеров — только Google/Firefox (Brave в DO_NOT_DELETE)
echo "=== 2. Windsurf полностью ===" echo "=== 2. Кэши браузеров (не Brave) ==="
cleanup_dir "$HOME/.codeium/windsurf" "Codeium/Windsurf кэш"
cleanup_dir "$HOME/.windsurf" "Windsurf данные"
cleanup_dir "$HOME/Library/Application Support/Windsurf" "Windsurf Application Support"
# Если .codeium пуст — можно удалить целиком
[ -d "$HOME/.codeium" ] && [ -z "$(ls -A "$HOME/.codeium" 2>/dev/null)" ] && rm -rf "$HOME/.codeium" && echo " ✅ .codeium (пустая) удалена"
echo ""
# 3. Кэши браузеров — только Google/Firefox (Brave в DO_NOT_DELETE)
echo "=== 3. Кэши браузеров (не Brave) ==="
cleanup_dir "$HOME/Library/Caches/Google" "Google кэш" cleanup_dir "$HOME/Library/Caches/Google" "Google кэш"
cleanup_dir "$HOME/Library/Caches/Firefox" "Firefox кэш" cleanup_dir "$HOME/Library/Caches/Firefox" "Firefox кэш"
echo "" echo ""
# 3b. Уровень 1 — лёгкие кэши и временные артефакты (~0.5 GB) # 3. Приложения — Notion, zoom, LibreOffice, Movavi, Ledger (Telegram в DO_NOT_DELETE)
echo "=== 3b. Уровень 1 (кэши приложений, Apple, hunabapp temp) ===" echo "=== 3. Кэши приложений ==="
cleanup_dir "$HOME/Library/Caches/responsivelyapp-updater" "responsivelyapp-updater кэш"
cleanup_dir "$HOME/Library/Application Support/ResponsivelyApp" "ResponsivelyApp кэш"
cleanup_dir "$HOME/Library/Caches/GeoServices" "GeoServices кэш"
cleanup_dir "$HOME/Library/Caches/com.apple.helpd" "com.apple.helpd кэш"
cleanup_dir "$HOME/Library/Caches/com.apple.CloudTelemetry" "com.apple.CloudTelemetry кэш"
cleanup_dir "$HOME/Library/Caches/com.apple.ctcategories.service" "com.apple.ctcategories.service кэш"
HUNAB_TEMP="$HOME/code/hunabapp-dev/temp"
if [ -d "$HUNAB_TEMP" ]; then
find "$HUNAB_TEMP" -maxdepth 1 -type f \( -name 'hunab-backend-bundle-*.tar.gz' -o -name 'hunab-backend-logs-*.tar.gz' \) -print0 2>/dev/null | while IFS= read -r -d '' f; do
size=$(du -sh "$f" 2>/dev/null | cut -f1)
echo "🗑️ Удаление hunabapp temp: $(basename "$f") ($size)..."
rm -f "$f" && echo " ✅ удалён" || echo " ⚠️ не удалось"
done
fi
echo " Build/dist в ~/code (кроме frontend/android)..."
find "$HOME/code" -type d \( -name "build" -o -name "dist" -o -name ".next" -o -name "out" \) \
-not -path "*/node_modules/*" \
-not -path "*/venv/*" \
-not -path "*/.venv/*" \
-not -path "*/site-packages/*" \
-not -path "*/env/*" \
-not -path "*/hunabapp-dev/frontend/android/*" \
-exec rm -rf {} + 2>/dev/null || true
echo " ✅ Build-артефакты уровня 1 очищены"
echo ""
# 4. Приложения — Notion, zoom, LibreOffice, Movavi, Ledger (Telegram в DO_NOT_DELETE)
echo "=== 4. Кэши приложений ==="
cleanup_dir "$HOME/Library/Application Support/Notion" "Notion" cleanup_dir "$HOME/Library/Application Support/Notion" "Notion"
cleanup_dir "$HOME/Library/Application Support/zoom.us" "zoom.us" cleanup_dir "$HOME/Library/Application Support/zoom.us" "zoom.us"
cleanup_dir "$HOME/Library/Application Support/LibreOffice" "LibreOffice" cleanup_dir "$HOME/Library/Application Support/LibreOffice" "LibreOffice"
@@ -89,8 +52,8 @@ cleanup_dir "$HOME/Library/Application Support/Movavi" "Movavi"
cleanup_dir "$HOME/Library/Application Support/Ledger Live" "Ledger Live" cleanup_dir "$HOME/Library/Application Support/Ledger Live" "Ledger Live"
echo "" echo ""
# 5. Cursor — только разрешённые кэши и state backup (~10 GB) # 4. Cursor — только разрешённые кэши и state backup (~10 GB)
echo "=== 5. Cursor кэши (только разрешённые) ===" echo "=== 4. Cursor кэши (только разрешённые) ==="
STATE_BACKUP="$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb.backup" STATE_BACKUP="$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb.backup"
if [ -f "$STATE_BACKUP" ]; then if [ -f "$STATE_BACKUP" ]; then
size=$(du -sh "$STATE_BACKUP" 2>/dev/null | cut -f1) size=$(du -sh "$STATE_BACKUP" 2>/dev/null | cut -f1)
@@ -109,35 +72,16 @@ cleanup_dir "$HOME/Library/Application Support/Cursor/Partitions" "Cursor Partit
cleanup_dir "$HOME/.cursor/projects" "Cursor projects" cleanup_dir "$HOME/.cursor/projects" "Cursor projects"
echo "" echo ""
# 6. Homebrew, .cache, пустые логи # 5. Homebrew, .cache, пустые логи
echo "=== 6. Homebrew, .cache, пустые логи ===" echo "=== 5. Homebrew, .cache, пустые логи ==="
cleanup_dir "$HOME/Library/Caches/anytype-updater" "anytype-updater кэш" cleanup_dir "$HOME/Library/Caches/anytype-updater" "anytype-updater кэш"
cleanup_dir "$HOME/Library/Caches/Homebrew" "Homebrew кэш" cleanup_dir "$HOME/Library/Caches/Homebrew" "Homebrew кэш"
cleanup_dir "$HOME/.cache" ".cache" cleanup_dir "$HOME/.cache" ".cache"
find "$HOME/Library/Logs" -type f -size 0 -delete 2>/dev/null && echo " ✅ Пустые логи удалены" || true find "$HOME/Library/Logs" -type f -size 0 -delete 2>/dev/null && echo " ✅ Пустые логи удалены" || true
echo "" echo ""
# 7. ScreenRecordings (по желанию) # 6. Build артефакты (исключая hunabapp-dev/frontend/android — в DO_NOT_DELETE)
echo "=== 7. Записи экрана ===" echo "=== 6. Build артефакты в ~/code (кроме android) ==="
if [ -d "$HOME/Library/ScreenRecordings" ]; then
count=$(find "$HOME/Library/ScreenRecordings" -type f 2>/dev/null | wc -l | tr -d ' ')
if [ "$count" -gt 0 ]; then
echo " Найдено файлов: $count"
read -p " Удалить все записи экрана? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
cleanup_dir "$HOME/Library/ScreenRecordings" "Записи экрана"
else
echo " ⏭️ Пропущено"
fi
else
echo " Папка пуста"
fi
fi
echo ""
# 8. Build артефакты (исключая hunabapp-dev/frontend/android — в DO_NOT_DELETE)
echo "=== 8. Build артефакты в ~/code (кроме android) ==="
read -p " Удалить build/dist/.next/out? (y/n): " -n 1 -r read -p " Удалить build/dist/.next/out? (y/n): " -n 1 -r
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
@@ -155,8 +99,8 @@ else
fi fi
echo "" echo ""
# 9. node_modules (опционально; НЕ frontend — в DO_NOT_DELETE) # 7. node_modules (опционально; НЕ frontend — в DO_NOT_DELETE)
echo "=== 9. node_modules (кроме hunabapp-dev/frontend) ===" echo "=== 7. node_modules (кроме hunabapp-dev/frontend) ==="
read -p " Удалить node_modules в проектах? (y/n): " -n 1 -r read -p " Удалить node_modules в проектах? (y/n): " -n 1 -r
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
@@ -171,8 +115,8 @@ else
fi fi
echo "" echo ""
# 10. UTM виртуальная машина — опционально # 8. UTM виртуальная машина — опционально
echo "=== 10. UTM виртуальная машина ===" echo "=== 8. UTM виртуальная машина ==="
if [ -d "$HOME/code/Parrot-home-mate-6.4_arm64.utm" ]; then if [ -d "$HOME/code/Parrot-home-mate-6.4_arm64.utm" ]; then
read -p " Удалить UTM виртуальную машину? (y/n): " -n 1 -r read -p " Удалить UTM виртуальную машину? (y/n): " -n 1 -r
echo echo

View File

@@ -15,7 +15,7 @@
| **Исполняемый файл** | `C:\GOG Games\Jagged Alliance 3\JA3.exe` | | **Исполняемый файл** | `C:\GOG Games\Jagged Alliance 3\JA3.exe` |
| **Bottle** | **GOG-Install** (`scripts/games/gog/bottle.env`) — не HoMM/Steam | | **Bottle** | **GOG-Install** (`scripts/games/gog/bottle.env`) — не HoMM/Steam |
| **Galaxy в Wine** | **не использовать** для JA3 — CEF, swap ([GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md)) | | **Galaxy в Wine** | **не использовать** для JA3 — CEF, swap ([GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md)) |
| **Запуск игры** | **CrossOver** `run-ja3-crossover.sh` — см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) | | **Запуск игры** | **`run-ja3.sh`** (Whisky GOG-Install) — см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) |
| **Steam.app / Steam.exe** | **не в контуре** — отдельный bottle HoMM; в GOG-bottle клиента Steam быть не должно | | **Steam.app / Steam.exe** | **не в контуре** — отдельный bottle HoMM; в GOG-bottle клиента Steam быть не должно |
### Не путать с Steam ### Не путать с Steam
@@ -126,33 +126,26 @@ JA3 на Windows использует **D3D12**. **DXVK-macOS** (только D3
| **Whisky / Gcenx Wine 11.9** | `d3d12.dll` = wined3d → **Vulkan** (`d3d12_get_vk_physical_device Could not find Vulkan physical device`). **Нет D3DMetal hooks** ([Gcenx#97](https://github.com/Gcenx/macOS_Wine_builds/issues/97)). | | **Whisky / Gcenx Wine 11.9** | `d3d12.dll` = wined3d → **Vulkan** (`d3d12_get_vk_physical_device Could not find Vulkan physical device`). **Нет D3DMetal hooks** ([Gcenx#97](https://github.com/Gcenx/macOS_Wine_builds/issues/97)). |
| **GPTK PE + Gcenx ntdll** | `__wine_unix_call` / `dxgi.dll not found` — PE DLL GPTK несовместимы с чужим Wine. | | **GPTK PE + Gcenx ntdll** | `__wine_unix_call` / `dxgi.dll not found` — PE DLL GPTK несовместимы с чужим Wine. |
| **GPTK Wine 7.7 alone** | `comctl32.dll failed to initialize` на этой macOS — не запускается даже notepad. | | **GPTK Wine 7.7 alone** | `comctl32.dll failed to initialize` на этой macOS — не запускается даже notepad. |
| **CrossOver 26 + D3DMetal** | **Работает.** Встроенные `apple_gptk` + `WINED3DMETAL=1`. | | **CrossOver 26 + D3DMetal** | Работало; **CrossOver удалён** (2026-06). |
| **Whisky + GPTK** | D3DMetal в GOG-Install bottle — см. `install-gptk-whisky.sh` |
Сообщения GOG API (`MatchmakingServers`, `Music`, `Game name not defined`) — **не причина** краша. Сообщения GOG API (`MatchmakingServers`, `Music`, `Game name not defined`) — **не причина** краша.
#### Канон запуска (подтверждено на этой машине) #### Канон запуска (2026-06)
```bash ```bash
brew install --cask crossover # один раз bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
``` ```
CrossOver bottle **JA3-GOG** (win10_64, D3DMetal on). Игра клонируется APFS clonefile в bottle (Wine **не читает** symlink за пределы prefix → `CommonLua/Core/autorun.lua not found`). Игра в bottle **GOG-Install** (Whisky). При D3D12-ошибках — GPTK в bottle ([JA3_RUNBOOK.md](JA3_RUNBOOK.md) §2).
**Whisky `run-ja3.sh` для D3D12 на Mac не работает** — только CrossOver (или Porting Kit GUI).
Альтернатива GUI: [Porting Kit](https://www.paulthetall.com/jagged-aliance-3-for-mac/) (тот же GPTK/D3DMetal стек).
### Запуск игры ### Запуск игры
```bash ```bash
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
``` ```
Whisky (`run-ja3.sh`) — только установка innoextract в GOG-Install; **графика D3D12 → CrossOver**. Обход swap (тормоза, на свой риск):
Обход (тормоза, на свой риск):
```bash ```bash
JA3_IGNORE_SWAP=1 bash ~/code/local_machine/scripts/games/gog/run-ja3.sh JA3_IGNORE_SWAP=1 bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
@@ -160,7 +153,7 @@ JA3_IGNORE_SWAP=1 bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
Cmd+Tab → `wine64` / `JA3`. В графике при артефактах: **отключить heat haze**. Cmd+Tab → `wine64` / `JA3`. В графике при артефактах: **отключить heat haze**.
**Ошибка «Failed to initialize graphics subsystem»:** Whisky/Gcenx Wine **не поддерживает D3DMetal** для d3d12. Используйте **CrossOver**: `setup-ja3-crossover.sh` + `run-ja3-crossover.sh`. Из Whisky Run **не запускать** — только CrossOver bottle JA3-GOG с `start /d` (CWD + CommonLua). **Ошибка «Failed to initialize graphics subsystem»:** установите GPTK и настройте D3DMetal в Whisky — см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §2. **Не** запускать `JA3.exe` из Whisky Run без `start /d`.
### Итог на диске (после успеха на этой машине) ### Итог на диске (после успеха на этой машине)
@@ -200,9 +193,8 @@ bash ~/code/local_machine/scripts/games/gog/install-ja3-offline.sh ~/Downloads/s
| `install-ja3-multipart.sh` (Wine `/VERYSILENT`) | `install-ja3-innoextract.sh` | | `install-ja3-multipart.sh` (Wine `/VERYSILENT`) | `install-ja3-innoextract.sh` |
| Распаковка в `scripts/games/cache/ja3-*` | сразу в `drive_c/GOG Games/…` | | Распаковка в `scripts/games/cache/ja3-*` | сразу в `drive_c/GOG Games/…` |
| Держать `.bin` после установки без нужды | скрипт удалит (~15 GB) | | Держать `.bin` после установки без нужды | скрипт удалит (~15 GB) |
| `launch-galaxy.sh` / Galaxy CEF для JA3 | **`run-ja3-crossover.sh`** ([JA3_RUNBOOK.md](JA3_RUNBOOK.md)) | | `launch-galaxy.sh` / Galaxy CEF для JA3 | **`run-ja3.sh`** ([JA3_RUNBOOK.md](JA3_RUNBOOK.md)) |
| Whisky `run-ja3.sh` для D3D12 | CrossOver + D3DMetal | | Whisky Run `JA3.exe` без CWD | `run-ja3.sh` (`start /d`) |
| `RetinaMode=y` + `LogPixels=192` для мыши JA3 | Клип курсора на середине экрана → **legacy** ([JA3_RUNBOOK.md](JA3_RUNBOOK.md) §9) |
| Жёсткий стоп JA3 при swap 800 MB без Wine | Устарело; warn до 4 GB — [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §4 | | Жёсткий стоп JA3 при swap 800 MB без Wine | Устарело; warn до 4 GB — [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §4 |
--- ---

View File

@@ -1,9 +1,11 @@
# Jagged Alliance 3 (GOG) — runbook Mac / Apple Silicon # Jagged Alliance 3 (GOG) — runbook Mac / Apple Silicon
**Машина:** M1 Pro, macOS Sonoma+, bottle Whisky **GOG-Install** (`9ee55e7c-a58e-411e-a93f-30530d4f1073`). **Машина:** M1 Pro, macOS Sonoma+, bottle Whisky **GOG-Install** (`9ee55e7c-a58e-411e-a93f-30530d4f1073`).
**Проверено:** 2026-05-28 (установка) · 2026-05-29 (запуск через CrossOver). **Проверено:** 2026-05-28 (установка) · 2026-06-17 (CrossOver удалён; запуск только Whisky).
Связанные документы: [JA3_MAC_WITHOUT_GALAXY.md](JA3_MAC_WITHOUT_GALAXY.md) (установка offline), [TROUBLESHOOTING.md](TROUBLESHOOTING.md), [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md). Связанные документы: [JA3_MAC_WITHOUT_GALAXY.md](JA3_MAC_WITHOUT_GALAXY.md), [TROUBLESHOOTING.md](TROUBLESHOOTING.md), [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md).
> **Примечание (2026-06):** CrossOver и связанные скрипты (`run-ja3-crossover.sh`, `JA3-GOG`, `JA3-WINE`, `sync-ja3-saves.sh`) **удалены** с машины. Запуск JA3 — только через Whisky GOG-Install.
--- ---
@@ -17,281 +19,93 @@ bash ~/code/local_machine/scripts/games/gog/install-ja3-innoextract.sh
bash ~/code/local_machine/scripts/games/gog/finish-ja3-layout.sh bash ~/code/local_machine/scripts/games/gog/finish-ja3-layout.sh
``` ```
### Запуск (каждый раз, CrossOver + D3DMetal) ### Запуск (каждый раз)
```bash ```bash
brew install --cask crossover # один раз bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh # один раз
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
``` ```
- **Cmd+Tab** → окно `JA3.exe` - **Cmd+Tab** → окно **`JA3.exe`**
- В настройках графики: **отключить heat haze** - **Swap:** «залипший» swap после Wine **не блокирует** запуск — только предупреждение. Стоп: swap ≥ **4096 MB** без Wine, или swap ≥ **1200 MB** пока **Wine/JA3 ещё работают**`reset-ja3-whisky.sh` / reboot. Обход: `JA3_IGNORE_SWAP=1`
- **Swap:** «залипший» swap после Wine (24 GB без процессов) **не блокирует** запуск — только предупреждение. Стоп: swap ≥ **4096 MB** без Wine, или swap ≥ **1200 MB** пока **Wine/JA3 ещё работают**`reset-ja3-crossover.sh` / reboot. Обход: `JA3_IGNORE_SWAP=1` - **Задержка cold start:** 1560 с до появления окна — норма
### Два «контура»
| Контур | Роль | Инструмент |
|--------|------|------------|
| **GOG-Install** (Whisky) | innoextract, файлы игры, CommonLua, реестр GOG | `install-ja3-innoextract.sh`, `finish-ja3-layout.sh` |
| **JA3-GOG** (CrossOver) | **запуск** D3D12 через D3DMetal | `setup-ja3-crossover.sh`, `run-ja3-crossover.sh` |
Whisky **`run-ja3.sh` не запускает JA3 с рабочей графикой** — только CrossOver (или Porting Kit GUI).
--- ---
## 2. Почему CrossOver, а не Whisky ## 2. Графика: Whisky vs D3D12
JA3 на Windows использует **DirectX 12**. На Apple Silicon единственный рабочий путь — **Apple D3DMetal** (Game Porting Toolkit), встроенный в **CrossOver 23+** с флагом `WINED3DMETAL=1`. JA3 на Windows использует **DirectX 12**. На Apple Silicon рабочий путь раньше был **CrossOver + D3DMetal** — CrossOver снят.
**Whisky** использует **Gcenx Wine** — maintainer явно пишет, что сборки **не содержат D3DMetal hooks** ([Gcenx/macOS_Wine_builds#97](https://github.com/Gcenx/macOS_Wine_builds/issues/97)). В этом случае `d3d12.dll` (94 KB) идёт через **wined3d → Vulkan**, а на Mac нет Vulkan physical device: **Сейчас:** `run-ja3.sh` запускает игру в **Whisky GOG-Install**:
- с **GPTK/D3DMetal** в bottle (если установлен Game Porting Toolkit) — D3D12;
```text - иначе **DXVK D3D11 fallback** (`-graphicsapi d3d11`).
fixme:d3d12:d3d12_get_vk_physical_device Could not find Vulkan physical device for DXGI adapter.
```
→ UI: **«Failed to initialize graphics subsystem»**
→ лог JA3: **`Failed creating render device`**
Копирование `D3DMetal.framework` + `libd3dshared.dylib` + моста `d3d12.so` **не достаточно**, если PE `d3d12.dll` / `ntdll` от Gcenx — unix-вызовы D3DMetal не подключены.
---
## 3. RCA: злоключения 2026-05-28 — 2026-05-29
Хронология попыток на этой машине (чат + логи `JA3.exe-*.log`, Wine debug).
### Фаза A — установка и ложные следы (28.05)
| # | Симптом / действие | Реальная причина | Итог |
|---|-------------------|------------------|------|
| A1 | GOG web-stub, пустые кнопки | Нет **gdiplus** (winetricks) | ✅ `install-gdiplus.sh` + corefonts |
| A2 | Galaxy CEF, окна нет | HWND/CEF в Wine, swap | Galaxy **не нужен** для JA3 offline |
| A3 | Лог `Steam initialized … Beta branch GOG` | GOG API-шим (`steam_api64.dll`), **не** Steam.app | ❌ не причина краша |
| A4 | `CreateSubsystem failed MatchmakingServers/Music` | GOG wrapper без Galaxy service | ❌ шум, не графика |
| A5 | `Game name not defined` | innoextract не выполнил Inno-постскрипты | ⚠️ косметика; фикс: `register-ja3-registry.sh` |
| A6 | Swap ~3 GB, скрипт блокирует запуск | «Залипший» swap после Wine; macOS не отдаёт без reboot | ✅ `gog_swap_guard_ja3_launch`: warn до 4 GB без процессов; reboot если Mac тормозит |
### Фаза B — «графика» в Whisky (2829.05)
| # | Попытка | Лог / ошибка | Почему не работает |
|---|---------|--------------|-------------------|
| B1 | DXVK + `-graphicsapi d3d11` | `Failed creating render device` | DXVK = D3D10/11, **не D3D12** |
| B2 | DXMT v0.80 | `dxgi.dll` **c000007b** | Несовместимость с Gcenx Wine 11 + overrides |
| B3 | `upgrade-whisky-wine-gcenx.sh` (11.9) + GPTK bridge | `d3d12_get_vk_physical_device` | Gcenx d3d12 = **Vulkan**, не Metal |
| B4 | GPTK PE `d3d12.dll`/`dxgi.dll` (114/86 KB) в `system32` | Всё ещё Vulkan path или `dxgi not found` | PE GPTK 7.7 ≠ ntdll Gcenx 11 |
| B5 | GPTK PE в `wine/lib/x86_64-windows` | `Library dxgi.dll not found` (c0000135) | `__wine_unix_call` не реализован в Gcenx ntdll |
| B6 | Замена `ntdll.so` на GPTK | `virtual_alloc_first_teb` / wine не стартует | Нельзя мешать ntdll разных Wine |
| B7 | `ditto` всего GPTK `lib/` в Whisky | Wine **7.7 GPTK** поверх bottle | Перезаписал `wine64` → comctl32 и др. |
| B8 | `prepare-ja3-d3dmetal.sh` (убрать d3d12 из system32) | Всё равно render device fail | Builtin Gcenx d3d12 без Metal |
| B9 | `forceD3D11: true` в Metadata.plist | Противоречие с D3D12 backend | Исправлено на `false` — без эффекта на Gcenx |
| B10 | GPTK Wine 7.7, отдельный prefix | `comctl32.dll failed to initialize` (c0000005) | GPTK Wine 7.7 **сломан** на этой macOS |
| B11 | `fixmywhisky.sh` (ditto GPTK lib) | Как B7 | Только D3DMetal.framework + bridge **без** подмены всего Wine |
### Фаза C — CrossOver (29.05, **успех**)
| # | Шаг | Результат |
|---|-----|-----------|
| C1 | `brew install --cask crossover` | CrossOver 26.1, встроенный `apple_gptk` |
| C2 | Bottle **JA3-GOG**, template **win10_64** (не win32 Wow64!) | `kernel32` c000007b на 32-bit bottle |
| C3 | `WINED3DMETAL=1`, `WINEDXVK=0` в `cxbottle.conf` | D3D12 → Metal |
| C4 | Symlink игры из Whisky bottle | `CommonLua/Core/autorun.lua: File Not Found` — Wine **не читает** symlink **вне** prefix |
| C5 | `cp -cR` (APFS clonefile) игры в CrossOver bottle | Файлы видны Wine; **~0** доп. места на APFS |
| C6 | `start /d "C:\GOG Games\Jagged Alliance 3" JA3.exe -unpacked` | CWD с `CommonLua/`; не `--cx-app` без `/d` |
| C7 | Запуск | **JA3.exe** стабильно работает, нет `Failed creating render device` |
| C8 | Курсор: смещение клика → `RetinaMode=y` + `LogPixels=192` | Курсор **не опускается ниже середины экрана** (Y делится пополам) | ✅ профиль **`legacy`**: `RetinaMode=n`, без LogPixels — **подтверждено 2026-05-29** |
### Сигнатуры в логах (диагностика)
| Строка | Значение |
|--------|----------|
| `Failed creating render device` | D3D12 не дошёл до GPU — Whisky/Gcenx, не CrossOver |
| `d3d12_get_vk_physical_device Could not find Vulkan` | Gcenx wined3d path — нужен CrossOver D3DMetal |
| `Failed to initialize graphics subsystem` (UI) | То же, пользовательский текст JA3 |
| `Error opening 'CommonLua/Core/autorun.lua'` | Нет CWD (`start /d`) или symlink вместо клона в bottle |
| `comctl32.dll failed to initialize` | GPTK Wine 7.7 — не использовать |
| `__wine_unix_call` / `aborting` | GPTK PE на чужом Wine |
| `dxgi.dll` **c000007b** | DXMT + неправильные overrides |
| `Language: English` без `Failed creating` | Графика OK; смотреть CommonLua / crash дальше |
| Курсор виден, клик смещён по Y | HiDPI без согласованного масштаба Wine | `fix-ja3-crossover-mouse.sh` → профиль **legacy** |
| Курсор **не ниже середины экрана** | `RetinaMode=y` + `LogPixels=192` / двойной DPI | **legacy** (не retina); перезапуск JA3 |
Логи JA3:
`…/AppData/Roaming/Jagged Alliance 3/logs/JA3.exe-*.log`
Whisky: `…/Containers/com.franke.Whisky/Bottles/9ee55e7c-…/`
CrossOver: `~/Library/Application Support/CrossOver/Bottles/JA3-GOG/`
---
## 4. Антипаттерны (полный список)
### Установка
| ❌ Не делать | ✅ Вместо |
|-------------|----------|
| GOG web-stub `GOG_Galaxy_Jagged_Alliance_3.exe` | Offline backup 5 частей в `~/Games/` |
| `install-ja3-multipart.sh` (Wine Inno) | `install-ja3-innoextract.sh` |
| Распаковка в `scripts/games/cache/` | Сразу в `drive_c/GOG Games/…` |
| Bottle **HoMM-OldenEra** (Steam) | Bottle **GOG-Install** |
| Удалять `steam_api64.dll` в папке игры | Это GOG-шим, **оставить** |
| Steam client в GOG-bottle | `purge-steam-from-bottle.sh` |
### Графика / Wine
| ❌ Не делать | ✅ Вместо |
|-------------|----------|
| Whisky `run-ja3.sh` для игры | **`run-ja3-crossover.sh`** |
| `upgrade-whisky-wine-gcenx.sh` «для D3DMetal» | Gcenx **не** поддерживает D3DMetal hooks |
| `ditto` всего GPTK `lib/` в Whisky Libraries | Только `D3DMetal.framework` + `libd3dshared` + `.so` bridge (см. `install-gptk-whisky.sh`) |
| GPTK PE `d3d12/dxgi` в system32 **Whisky** | CrossOver со встроенным `apple_gptk` |
| Замена `ntdll.so` GPTK в Gcenx Wine | Ломает preloader |
| DXVK / DXMT для JA3 как «замена D3D12» | Не подтверждено; JA3 = D3D12 |
| `dxgi,d3d11=n,b` с DXMT | Ломает цепочку → c000007b |
| Whisky Run **`JA3.exe`** без CWD | `start /d` или `run-ja3.cmd` (Whisky) / CrossOver `start /d` |
| Symlink игры из Whisky в CrossOver bottle | **`cp -cR`** (APFS clone) |
| CrossOver bottle **win32** / Wow64 | **`win10_64`** |
| `--cx-app JA3.exe` без `start /d` | `start /d "…Jagged Alliance 3" JA3.exe -unpacked` |
| `forceD3D11: true` + backend d3dMetal | `forceD3D11: false` (Metadata Whisky — не решает Gcenx) |
### Окружение
| ❌ Не делать | ✅ Вместо |
|-------------|----------|
| Считать «залипший» swap 24 GB блокером JA3 | Запуск разрешён (warn); reboot — если Mac тормозит |
| Запуск JA3 при swap ≥ **1200 MB** + **Wine/JA3 ещё работают** | `reset-ja3-crossover.sh` → reboot |
| Запуск JA3 при swap ≥ **4096 MB** без Wine | Reboot Mac или `JA3_IGNORE_SWAP=1` |
| Жёсткий лимит **800 MB** для `run-ja3-crossover.sh` | Устарело; см. `gog_swap_guard_ja3_launch` в `config.sh` |
| Путать лог Galaxy с JA3 | JA3 offline **без** Galaxy UI |
| Чинить «graphics» через Galaxy service | `MatchmakingServers`/`Music` — шум GOG API |
### Мышь (CrossOver / Retina)
| ❌ Не делать | ✅ Вместо |
|-------------|----------|
| `RetinaMode=y` + `LogPixels=192` + `Win8DpiScaling=1` «по умолчанию» | **`fix-ja3-crossover-mouse.sh`** — профиль **legacy** (`RetinaMode=n`) |
| CrossOver GUI **High Resolution Mode** без проверки | Если клип Y на середине → **legacy** |
| Менять реестр мыши без перезапуска JA3 | `reset-ja3-crossover.sh``run-ja3-crossover.sh` |
| Профиль **retina** при клипе на середине экрана | **legacy**; при смещении клика — **retina-lite** |
| Ожидать фикс мыши «на лету» | Workaround: верхний левый угол окна = угол экрана |
---
## 5. Скрипты
| Скрипт | Назначение | Статус |
|--------|------------|--------|
| `install-ja3-innoextract.sh` | Multipart offline → bottle | ✅ канон установки |
| `finish-ja3-layout.sh` | CommonLua, реестр, upscale DLL | ✅ после innoextract |
| `setup-ja3-crossover.sh` | Bottle JA3-GOG, clone игры, D3DMetal | ✅ один раз |
| **`run-ja3-crossover.sh`** | **Запуск игры** | ✅ **канон** |
| `reset-ja3-crossover.sh` | Stop JA3 / Wine перед перезапуском | ✅ |
| `fix-ja3-crossover-mouse.sh` | Профили мыши (legacy / retina-lite / retina) | ✅ default **legacy** |
| `run-ja3.sh` | Whisky (эксперимент; графика fail) | ⚠️ только с предупреждением |
| `prepare-ja3-d3dmetal.sh` | Whisky D3DMetal prep | ⚠️ не решает Gcenx d3d12 |
| `upgrade-whisky-wine-gcenx.sh` | Wine 11.9 | ❌ для JA3 не использовать |
| `run-ja3-gptk-wine.sh` | GPTK Wine 7.7 prefix | ❌ comctl32 на этой Mac |
| `install-ja3-dxmt.sh` | DXMT fallback d3d11 | ❌ c000007b |
| `emergency-stop.sh` | Kill Wine/GOG перед запуском | ✅ |
---
## 6. CrossOver bottle (детали)
**Путь:** `~/Library/Application Support/CrossOver/Bottles/JA3-GOG/`
**cxbottle.conf** (минимум):
```ini
"WINED3DMETAL" = "1"
"WINEDXVK" = "0"
```
**Игра:** `drive_c/GOG Games/Jagged Alliance 3/` — APFS-клон из Whisky GOG-Install (не symlink).
**Проверка:**
При ошибке `Failed to initialize graphics subsystem` / `dxgi.dll not found`:
```bash ```bash
pgrep -fl 'JA3\.exe' brew install --cask gcenx/wine/game-porting-toolkit
grep -E 'Failed creating|autorun|CRASH' \ bash ~/code/local_machine/scripts/games/gog/install-gptk-whisky.sh
"$HOME/Library/Application Support/CrossOver/Bottles/JA3-GOG/drive_c/users/"*/AppData/Roaming/Jagged\ Alliance\ 3/logs/*.log | tail bash ~/code/local_machine/scripts/games/gog/configure-ja3-d3dmetal.sh
bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
``` ```
--- ---
## 7. Альтернативы ## 3. Пути на диске
| Вариант | Комментарий | | Что | Путь |
|---------|-------------| |-----|------|
| [Porting Kit](https://www.paulthetall.com/jagged-aliance-3-for-mac/) + GOG offline | Тот же GPTK/D3DMetal; GUI, «просто работает» на M-series | | Игра (Whisky) | `…/Containers/com.franke.Whisky/Bottles/9ee55e7c-…/drive_c/GOG Games/Jagged Alliance 3/` |
| CrossOver GUI | Bottle JA3-GOG → Install → Run exe; D3DMetal в Advanced Settings | | Сейвы | `…/drive_c/users/eternal/AppData/Roaming/Jagged Alliance 3/<account_id>/` |
| Whisky + GPTK drag DMG | [Whisky#124](https://github.com/Whisky-App/Whisky/issues/124) — для **DX12** на Gcenx всё равно нет hooks | | Логи JA3 | `…/AppData/Roaming/Jagged Alliance 3/logs/JA3.exe-*.log` |
--- ---
## 9. Курсор: HiDPI / Retina (CrossOver) ## 4. Скрипты
**Проверено 2026-05-29** на M1 Pro + CrossOver 26.1, bottle **JA3-GOG**. | Скрипт | Назначение |
|--------|------------|
| `install-ja3-innoextract.sh` | Установка из offline backup |
| `finish-ja3-layout.sh` | CommonLua, реестр GOG |
| **`run-ja3.sh`** | **Канон запуска** |
| `reset-ja3-whisky.sh` | Остановить JA3/Wine в GOG-Install |
| `emergency-stop.sh` | Полный стоп GOG/Wine |
| `prepare-ja3-d3dmetal.sh` / `configure-ja3-d3dmetal.sh` | D3DMetal в Whisky |
### Симптомы **Удалено (CrossOver):** `run-ja3-crossover.sh`, `setup-ja3-crossover.sh`, `run-ja3-wine.sh`, `sync-ja3-saves.sh`, `fix-ja3-crossover-mouse.sh`.
| Симптом | Типичная причина |
|---------|------------------|
| Клик «выше/ниже» UI, курсор виден | Wine отдаёт logical pixels, игра рисует в другом масштабе |
| **Курсор не опускается ниже середины экрана** | `RetinaMode=y` + `LogPixels=192` — координата Y делится пополам |
### Профили (`fix-ja3-crossover-mouse.sh`)
| Профиль | Реестр | Когда |
|---------|--------|-------|
| **`legacy`** (default) | `RetinaMode=n`, LogPixels/Win8Dpi **удалены** | **Канон** — полный ход курсора по Y; возможен небольшой сдвиг клика |
| `retina-lite` | `RetinaMode=y`, без LogPixels | Если legacy смещает клик, но **нет** клипа на середине |
| `retina` | `RetinaMode=y`, `LogPixels=192`, `Win8DpiScaling=1` | ❌ часто **клип Y на 50%** — не использовать как default |
```bash
# Канон (после смены профиля — перезапуск JA3)
bash ~/code/local_machine/scripts/games/gog/fix-ja3-crossover-mouse.sh
bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
# Если legacy смещает клик
JA3_MOUSE_PROFILE=retina-lite bash ~/code/local_machine/scripts/games/gog/fix-ja3-crossover-mouse.sh
bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
```
**Без перезапуска (workaround):** перетащите окно JA3 так, чтобы **верхний левый угол окна** совпал с **верхним левым углом экрана**.
CrossOver GUI: Bottle **JA3-GOG****High Resolution Mode** — при клипе на середине **выключить** (эквивалент **legacy**).
--- ---
## 10. Быстрые команды (терминал) ## 5. Антипаттерны
| Не делать | Вместо |
|-----------|--------|
| `launch-galaxy.sh` / Galaxy CEF для JA3 | `run-ja3.sh` |
| Whisky Run **`JA3.exe`** без CWD | `run-ja3.sh` (`start /d`) |
| Дважды подряд `run-ja3.sh` без reset при живом JA3 | `reset-ja3-whisky.sh` |
| Galaxy web-stub для JA3 | offline innoextract |
---
## 6. Быстрые команды
| Действие | Команда | | Действие | Команда |
|----------|---------| |----------|---------|
| **Запуск** | `bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh` | | **Запуск** | `bash ~/code/local_machine/scripts/games/gog/run-ja3.sh` |
| **Стоп / reset Wine** | `bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh` | | **Стоп** | `bash ~/code/local_machine/scripts/games/gog/reset-ja3-whisky.sh` |
| **Стоп всё GOG/Wine** | `bash ~/code/local_machine/scripts/games/gog/emergency-stop.sh` | | Стоп всё GOG/Wine | `bash ~/code/local_machine/scripts/games/gog/emergency-stop.sh` |
| **Первичная настройка bottle** | `bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh` | | Проверить процесс | `pgrep -fl 'JA3\.exe'` |
| **Фикс мыши** | `bash ~/code/local_machine/scripts/games/gog/fix-ja3-crossover-mouse.sh` | | Обход swap | `JA3_IGNORE_SWAP=1 bash …/run-ja3.sh` |
| Swap «залип» 24 GB, Wine нет | просто `run-ja3-crossover.sh` (warn) |
| Swap + Wine/JA3 живы ≥ 1200 MB | `reset-ja3-crossover.sh` → reboot |
| Swap ≥ 4096 MB без Wine | reboot Mac |
| Обход swap | `JA3_IGNORE_SWAP=1 bash …/run-ja3-crossover.sh` |
| Профиль мыши | `JA3_MOUSE_PROFILE=legacy\|retina-lite\|retina` + fix + reset + run |
**Полный цикл перезапуска:** **Полный цикл:**
```bash ```bash
bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh bash ~/code/local_machine/scripts/games/gog/reset-ja3-whisky.sh
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
``` ```
--- ---
## 8. Ссылки ## 7. Ссылки
- [Gcenx — нет D3DMetal в packages](https://github.com/Gcenx/macOS_Wine_builds/issues/97) - [Gcenx — нет D3DMetal в packages](https://github.com/Gcenx/macOS_Wine_builds/issues/97)
- [CrossOver — D3DMetal advanced settings](https://support.codeweavers.com/miscellanous/advanced-settings-in-crossover-mac-26)
- [Whisky — GPU / GPTK tracking](https://github.com/Whisky-App/Whisky/issues/124) - [Whisky — GPU / GPTK tracking](https://github.com/Whisky-App/Whisky/issues/124)
- [Proton JA3 — graphics / LocalStorage](https://github.com/ValveSoftware/Proton/issues/7039) (Linux; отражения Off/Low — аналог для Wine при других GPU) - [Proton JA3 — graphics](https://github.com/ValveSoftware/Proton/issues/7039)

View File

@@ -30,9 +30,9 @@ GOG web-stub: bottle **`GOG-Install`** (`scripts/games/gog/bottle.env`) — **н
| **GOG Galaxy: окна нет**, скрипт OK | offline installer JA3; Galaxy — best effort | [§ GOG Galaxy — окно](#gog-galaxy-окно-не-открывается) · [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) | | **GOG Galaxy: окна нет**, скрипт OK | offline installer JA3; Galaxy — best effort | [§ GOG Galaxy — окно](#gog-galaxy-окно-не-открывается) · [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) |
| GOG Galaxy → **swap**, Mac «встал» | `gog/emergency-stop.sh` → перезагрузка | [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) § Swap | | GOG Galaxy → **swap**, Mac «встал» | `gog/emergency-stop.sh` → перезагрузка | [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) § Swap |
| GOG stub: «запустил, окна нет» | **Cmd+Tab**`GOG_Galaxy_*.exe` / `wine64` | [§ GOG — проверка успеха](#gog-web-stub-отдельный-bottle) | | GOG stub: «запустил, окна нет» | **Cmd+Tab**`GOG_Galaxy_*.exe` / `wine64` | [§ GOG — проверка успеха](#gog-web-stub-отдельный-bottle) |
| **JA3: Failed to initialize graphics subsystem** | CrossOver D3DMetal, не Whisky | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §24 | | **JA3: Failed to initialize graphics subsystem** | GPTK/D3DMetal в Whisky или D3D11 fallback | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §2 |
| **JA3: курсор не ниже середины / смещение клика** | профиль мыши **legacy** vs retina | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §9 | | **JA3: `run-ja3.sh` — сразу `%`, окна нет** | задержка cold start + `emergency-stop`**норма** | [§ JA3 — запуск](#ja3--запуск-whisky) |
| **JA3 без Galaxy** | install → Whisky; **запуск → CrossOver** | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) | | **JA3: повторный запуск не идёт** | swap-guard + JA3/Wine ещё живы | `reset-ja3-whisky.sh` |
| Установить через web-stub | только GalaxySetup (ломается в Wine) | [§ GOG web-stub](#gog-web-stub-отдельный-bottle) | | Установить через web-stub | только GalaxySetup (ломается в Wine) | [§ GOG web-stub](#gog-web-stub-отдельный-bottle) |
| `fork failed`, Mac «встал», swap 20+ GB | `winedbg` / Steam assert loop → § Swap | [§ Swap и winedbg](#захламление-swap-и-утечка-winedbg) | | `fork failed`, Mac «встал», swap 20+ GB | `winedbg` / Steam assert loop → § Swap | [§ Swap и winedbg](#захламление-swap-и-утечка-winedbg) |
| «20 GB съело» после GOG | обычно **не** stub — swap + дампы Steam | [§ Swap](#захламление-swap-и-утечка-winedbg) · [§ GOG](#gog-web-stub-отдельный-bottle) | | «20 GB съело» после GOG | обычно **не** stub — swap + дампы Steam | [§ Swap](#захламление-swap-и-утечка-winedbg) · [§ GOG](#gog-web-stub-отдельный-bottle) |
@@ -110,6 +110,9 @@ GOG web-stub: bottle **`GOG-Install`** (`scripts/games/gog/bottle.env`) — **н
| Только **`install-corefonts`**, без **gdiplus** | `.ttf` есть, лог всё равно `FontFamilyNotFound` | оба скрипта + порядок ниже | | Только **`install-corefonts`**, без **gdiplus** | `.ttf` есть, лог всё равно `FontFamilyNotFound` | оба скрипта + порядок ниже |
| **`setup-wine-bottle.sh`** для GOG web-stub | DWrite/Steam-реестр, **нет** gdiplus | **`gog/setup-bottle.sh`** | | **`setup-wine-bottle.sh`** для GOG web-stub | DWrite/Steam-реестр, **нет** gdiplus | **`gog/setup-bottle.sh`** |
| **`setup-gog-bottle.sh`** (HoMM) для JA3 | устаревшая обёртка | **`gog/setup-bottle.sh`** | | **`setup-gog-bottle.sh`** (HoMM) для JA3 | устаревшая обёртка | **`gog/setup-bottle.sh`** |
| **Whisky** Run `JA3.exe` в Whisky без `run-ja3.sh` | `dxgi.dll not found`, CWD/autorun | **`run-ja3.sh`** |
| Считать мгновенный `%` после `run-ja3.sh` сбоем | `wine start` не блокирует Terminal | **Cmd+Tab**`JA3.exe`; ждать до ~60 с |
| Два `run-ja3.sh` подряд без reset | swap ≥1200 MB + живой JA3 | `reset-ja3-whisky.sh` |
| **winetricks corefonts** из Whisky GUI | 404 на CDN Microsoft | SourceForge в **`gog/install-corefonts.sh`**; macOS Fonts | | **winetricks corefonts** из Whisky GUI | 404 на CDN Microsoft | SourceForge в **`gog/install-corefonts.sh`**; macOS Fonts |
| **Debian-only** URL `msttcorefonts` | 404 | primary: `downloads.sourceforge.net/corefonts/…` | | **Debian-only** URL `msttcorefonts` | 404 | primary: `downloads.sourceforge.net/corefonts/…` |
| **`winetricks liberation`** как единственный шаг | мало гарантий для VGUI/.NET | macOS + corefonts + профиль DLL | | **`winetricks liberation`** как единственный шаг | мало гарантий для VGUI/.NET | macOS + corefonts + профиль DLL |
@@ -713,7 +716,45 @@ A/B CEF: `GOG_CEF_PROFILE=steam bash …/launch-galaxy-ui.sh` (добавляе
| Смотреть только Whisky, не Cmd+Tab | окно Wine **вне** главного окна Whisky | | Смотреть только Whisky, не Cmd+Tab | окно Wine **вне** главного окна Whisky |
| Запуск из `~/code/local_machine` без `cd drive_c` | `launch-galaxy.sh` сам ставит cwd в `drive_c` | | Запуск из `~/code/local_machine` без `cd drive_c` | `launch-galaxy.sh` сам ставит cwd в `drive_c` |
**JA3 на Mac:** установка — offline backup + `install-ja3-innoextract.sh` (Whisky GOG-Install). **Запуск — CrossOver D3DMetal:** `setup-ja3-crossover.sh` + `run-ja3-crossover.sh`. Whisky `run-ja3.sh` **не** даёт рабочую D3D12. Runbook: [JA3_RUNBOOK.md](JA3_RUNBOOK.md). **JA3 на Mac:** установка — offline backup + `install-ja3-innoextract.sh` (Whisky GOG-Install). **Запуск:** `run-ja3.sh`. Runbook: [JA3_RUNBOOK.md](JA3_RUNBOOK.md).
---
## JA3 — запуск (Whisky)
**Канон:**
```bash
bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
```
`run-ja3.sh` запускает JA3 в bottle **GOG-Install** (Whisky). CrossOver **удалён** (2026-06).
### «Не пускает» vs «задержка»
| Что видите | Это ошибка? | Действие |
|------------|-------------|----------|
| Строки «Запуск JA3…» и сразу `%` | **Нет**`wine start` не держит Terminal | Подождать 1560 с, **Cmd+Tab**`JA3.exe` |
| Пауза 2040 с **до** строк «Запуск JA3…» | **Нет**`emergency-stop` + `sleep 2` | Норма при каждом запуске |
| `❌ Swap … Wine/JA3 ещё работают` | **Да** — повторный запуск при живой игре | `reset-ja3-whisky.sh`, затем снова `run-ja3.sh` |
| Окна нет >60 с, `pgrep JA3` пусто | **Да** | Лог в `…/GOG-Install/…/AppData/Roaming/Jagged Alliance 3/logs/JA3.exe-*.log` |
### Проверка
```bash
pgrep -fl 'JA3\.exe'
ls -t ~/Library/Containers/com.franke.Whisky/Bottles/*/drive_c/users/*/AppData/Roaming/Jagged\ Alliance\ 3/logs/*.log 2>/dev/null | head -1 | xargs tail -20
sysctl vm.swapusage
```
### Антипаттерны (JA3 запуск)
| ❌ | ✅ |
|----|-----|
| Whisky Run **`JA3.exe`** без CWD | `run-ja3.sh` (`start /d`) |
| Ждать блокировку Terminal | Cmd+Tab → `JA3.exe` |
| Два запуска без reset при живом JA3 | `reset-ja3-whisky.sh` между сессиями |
| CrossOver / `run-ja3-crossover.sh` | удалено — только `run-ja3.sh` |
### Setup: красный X, пустое окно, gdi32 (RCA 2026-05-28) ### Setup: красный X, пустое окно, gdi32 (RCA 2026-05-28)
@@ -959,4 +1000,4 @@ pkill -9 winedbg; pkill -9 wineserver
--- ---
*Последняя сверка: 2026-05-28 (§ шрифты: сводка антипаттернов Steam dwrite / GOG gdiplus; GOG-Install stub OK; GalaxySetup → gdi32) · Whisky 3 · WhiskyWine 11* *Последняя сверка: 2026-06-17 (CrossOver удалён; JA3 только Whisky) · 2026-05-28 (шрифты Steam dwrite / GOG gdiplus) · Whisky 3*

View File

@@ -30,7 +30,7 @@
| **GOG Galaxy (лаунчер)** | Galaxy 2.0 | `start.sh gog-install` или `gog/install-galaxy-extract.sh` | `gog` | | **GOG Galaxy (лаунчер)** | Galaxy 2.0 | `start.sh gog-install` или `gog/install-galaxy-extract.sh` | `gog` |
| **GOG web-stub** | `GOG_Galaxy_*.exe` | Только bottle `gog/` — [gog/README.md](../../scripts/games/gog/README.md) | `gog` | | **GOG web-stub** | `GOG_Galaxy_*.exe` | Только bottle `gog/` — [gog/README.md](../../scripts/games/gog/README.md) | `gog` |
| **Офлайн setup .exe** | backup с gog.com | Скопировать в `drive_c/`, запустить установщик | `plain` или `gog` | | **Офлайн setup .exe** | backup с gog.com | Скопировать в `drive_c/`, запустить установщик | `plain` или `gog` |
| **GOG multipart + innoextract** | JA3 offline backup | `gog/install-ja3-innoextract.sh` — [JA3 runbook](JA3_RUNBOOK.md) | **CrossOver:** `run-ja3-crossover.sh` | | **GOG multipart + innoextract** | JA3 offline backup | `gog/install-ja3-innoextract.sh` — [JA3 runbook](JA3_RUNBOOK.md) | `gog/run-ja3.sh` |
| **.NET WinForms installer** | web-stub, мастера GOG | **`gog/install-gdiplus.sh`** + corefonts | `gog` | | **.NET WinForms installer** | web-stub, мастера GOG | **`gog/install-gdiplus.sh`** + corefonts | `gog` |
| **CEF (Steam/GOG UI)** | Steam.exe, GalaxyClient | CEF-флаги в `config.sh` / `gog/config.sh` | `steam` / `gog` | | **CEF (Steam/GOG UI)** | Steam.exe, GalaxyClient | CEF-флаги в `config.sh` / `gog/config.sh` | `steam` / `gog` |
| **DX11 игра (Unity и т.д.)** | Olden Era | `install-dxvk-macos.sh` | `game` | | **DX11 игра (Unity и т.д.)** | Olden Era | `install-dxvk-macos.sh` | `game` |
@@ -97,7 +97,7 @@ bash …/run-wine-exe.sh --background --log /tmp/my-app.log --profile plain 'C:\
| Место на диске | `bash …/start.sh disk` | | Место на диске | `bash …/start.sh disk` |
| GOG bottle: клиент | `bash …/gog/launch-galaxy.sh` | | GOG bottle: клиент | `bash …/gog/launch-galaxy.sh` |
| GOG bottle: JA3 установка | `bash …/gog/install-ja3-innoextract.sh` | | GOG bottle: JA3 установка | `bash …/gog/install-ja3-innoextract.sh` |
| GOG bottle: JA3 запуск | `bash …/gog/run-ja3-crossover.sh` — [JA3_RUNBOOK.md](JA3_RUNBOOK.md) | | GOG bottle: JA3 запуск | `bash …/gog/run-ja3.sh` — [JA3_RUNBOOK.md](JA3_RUNBOOK.md) |
Полный список: `bash …/start.sh` без аргументов. Полный список: `bash …/start.sh` без аргументов.
@@ -147,7 +147,7 @@ export WINEMSYNC=1 WINEESYNC=1
| **Steam** | HoMM-OldenEra | `start.sh steam-ui` | Не параллельно с Steam.app | | **Steam** | HoMM-OldenEra | `start.sh steam-ui` | Не параллельно с Steam.app |
| **HoMM Olden Era** | HoMM-OldenEra | `start.sh play` | Не Play в Steam UI; нужен DXVK — §7 | | **HoMM Olden Era** | HoMM-OldenEra | `start.sh play` | Не Play в Steam UI; нужен DXVK — §7 |
| **GOG Galaxy 2.0** | HoMM-OldenEra | `start.sh gog` | Логин в Wine — best effort | | **GOG Galaxy 2.0** | HoMM-OldenEra | `start.sh gog` | Логин в Wine — best effort |
| **Jagged Alliance 3** | GOG-Install + CrossOver **JA3-GOG** | `gog/run-ja3-crossover.sh` | Установка: Whisky `install-ja3-innoextract.sh` ([runbook](JA3_RUNBOOK.md)) | | **Jagged Alliance 3** | GOG-Install (Whisky) | `gog/run-ja3.sh` | Установка: `install-ja3-innoextract.sh` ([runbook](JA3_RUNBOOK.md)) |
| **GOG Galaxy 2.0** | GOG-Install | `gog/launch-galaxy.sh` | Best effort; JA3 — **не** через Galaxy на этом Mac | | **GOG Galaxy 2.0** | GOG-Install | `gog/launch-galaxy.sh` | Best effort; JA3 — **не** через Galaxy на этом Mac |
| **Любой новый .exe** | свой / общий | `run-wine-exe.sh` | §2.1 | | **Любой новый .exe** | свой / общий | `run-wine-exe.sh` | §2.1 |

View File

@@ -132,7 +132,7 @@ export_wine_env
1. Пять файлов в **`~/Games/`**: `setup_jagged_alliance_3_1.5.2_(80730).exe` + `-1.bin``-4.bin`. 1. Пять файлов в **`~/Games/`**: `setup_jagged_alliance_3_1.5.2_(80730).exe` + `-1.bin``-4.bin`.
2. Bottle **GOG-Install** (`gog/bottle.env`). 2. Bottle **GOG-Install** (`gog/bottle.env`).
3. `bash …/gog/install-ja3-innoextract.sh` — распаковка в `drive_c/GOG Games/Jagged Alliance 3/`, `.bin` удаляются по мере чтения, после успеха — все архивы в `~/Games` (~15 GB). 3. `bash …/gog/install-ja3-innoextract.sh` — распаковка в `drive_c/GOG Games/Jagged Alliance 3/`, `.bin` удаляются по мере чтения, после успеха — все архивы в `~/Games` (~15 GB).
4. Запуск: `bash …/gog/run-ja3-crossover.sh` (swap guard: см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §4, §10). 4. Запуск: `bash …/gog/run-ja3.sh` (swap guard: см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §1, §6).
Полный runbook: [JA3_MAC_WITHOUT_GALAXY.md](JA3_MAC_WITHOUT_GALAXY.md). Полный runbook: [JA3_MAC_WITHOUT_GALAXY.md](JA3_MAC_WITHOUT_GALAXY.md).

View File

@@ -0,0 +1,3 @@
# Перенесено
Документация и скрипты для Fedora на старом MacBook Pro Intel — в **[../old_mac/README.md](../old_mac/README.md)**.

View File

@@ -0,0 +1,215 @@
# MacBook Pro M1 — зависания после замены матрицы
**Контекст:** заменена матрица (Built-in Liquid Retina XDR). После ремонта — повторяющиеся полные зависания системы.
**Инцидент:** 2026-06-16, ~4 зависания за час (21:3121:43).
**Проверено:** 2026-06-16, логи `/Library/Logs/DiagnosticReports/`, `log show`, `last reboot`.
См. также: [README.md](./README.md)
---
## 1. Симптом
| Что видит пользователь | Что в логах |
|------------------------|-------------|
| Экран и ввод «замирают», курсор не двигается | Нет записей в unified log на время freeze (дыра в логах) |
| Помогает только **удержание кнопки питания** | `btn_rst` / `finger_reset` в `forceReset-full-*.diag` |
| Зависания повторяются **сразу после перезагрузки** (через 29 мин) | Аптайм в отчётах: 155 с, 537 с |
| Не kernel panic с диалогом «перезагрузить» | Нет свежих `panic-full-*.panic` за 16.06; только `forceReset` |
Это **hard lockup** (полная неотзывчивость SoC), а не обычный краш приложения.
---
## 2. Хронология 2026-06-16
| Время | Событие | Аптайм до freeze |
|-------|---------|------------------|
| ~19:19 | Загрузка (сессия до первого сбоя) | — |
| 21:2421:26 | Telegram: отчёт `cpu_resource` (~55% CPU за 3 мин) | ~2 ч |
| **21:31** | `forceReset-full-2026-06-16-213115` · `btn_rst` | **~7921 с (~2 ч 12 мин)** |
| **21:34** | `forceReset-full-2026-06-16-213425` · `btn_rst` | **~155 с (~2.5 мин)** |
| 21:3421:37 | `deleted` (CacheDelete): `cpu_resource` (~52% CPU) | после 2-го reboot |
| **21:43** | `forceReset-full-2026-06-16-214334` · `btn_rst` | **~537 с (~9 мин)** |
`last reboot`:
```
reboot Tue Jun 16 21:43
reboot Tue Jun 16 21:34
reboot Tue Jun 16 21:31
```
В логах — **пропуск ~18 мин** (21:33 → 21:51): пока система зависла, запись остановилась.
---
## 3. RCA — что зафиксировано в отчётах
### 3.1 Тип сбоя: принудительный reset
Все три отчёта за 16.06:
```
panic(cpu N caller ...): btn_rst
Debugger message: panic
Boot faults: btn_rst, finger_reset, force_off (в ResetCounter)
```
`btn_rst` = ядро зафиксировало сброс **кнопкой питания**, когда система уже не отвечала. Это не программный panic с stack trace приложения.
Файлы:
- `/Library/Logs/DiagnosticReports/forceReset-full-2026-06-16-213115.0002.diag`
- `/Library/Logs/DiagnosticReports/forceReset-full-2026-06-16-213425.0002.diag`
- `/Library/Logs/DiagnosticReports/forceReset-full-2026-06-16-214334.0002.diag`
- `/Library/Logs/DiagnosticReports/ResetCounter-2026-06-16-213129.diag` (и 213438, 214346)
### 3.2 Главный подозреваемый: WindowServer (графический стек)
Снимок процессов **на момент зависания** (поле `systemTimeTask`):
| Freeze | Аптайм | WindowServer CPU time | Паттерн потоков |
|--------|--------|----------------------|-----------------|
| 21:31 | ~2 ч 12 мин | **714 с** | потоки ждут `semaphore port ... owned by pid` **самого WindowServer** |
| 21:34 | ~2.5 мин | **15 с** | то же — deadlock на semaphore |
| 21:43 | ~9 мин | **49 с** | то же — deadlock на semaphore |
WindowServer — процесс отрисовки всего UI (SkyLight). Потоки, ждущие semaphore у того же PID, — типичный **deadlock в графическом конвейере**.
Рядом в снимках присутствуют процессы дисплейного стека:
- `corebrightnessd` — яркость / подсветка
- `IOMFB_bics_daemon` — framebuffer (IOMFB)
- `backboardd` — события ввода / дисплей
### 3.3 Что **не** найдено в открытых логах
Поиск за 3 ч по `DCP`, `IOMFB`, `CLCD`, `backlight`, `GPU reset`, `panel`, `LCD`:
- **Нет** явных ошибок драйвера дисплея или DCP
- Только штатные записи (`mobileassetd`, `backlightservices`, уведомления)
Apple часто **не пишет** аппаратные ошибки шлейфа/матрицы в открытый unified log. Отсутствие строк не опровергает аппаратную причину.
### 3.4 Дополнительная нагрузка (усиливающие факторы)
| Процесс | Когда | Наблюдение |
|---------|-------|------------|
| **Telegram** | 21:2421:26 | `cpu_resource`: ~55% CPU за 162 с |
| **deleted** (CacheDelete) | 21:3421:37 | `cpu_resource`: ~52% CPU, 88 с после boot |
| **mds** / Spotlight | после каждого reboot | активная индексация (`mdworker_shared`) |
| **Память** | на момент проверки | 16 GB почти заняты, ~7.8 GB в compressor, свободно ~140 MB |
Высокая нагрузка и давление на память **усиливают** нестабильность, но паттерн WindowServer-deadlock + быстрые повторы после reboot указывают на проблему **графики/дисплея**, а не только на фоновые процессы.
### 3.5 История до 16.06 (система нестабильна несколько дней)
| Период | Тип отчётов | Характер |
|--------|-------------|----------|
| 1011.06 | `shutdown_stall` | зависание при выключении |
| 1214.06 | `panic-full` + `shutdown_stall` | `Wake transition timed out after 35 seconds` · suspected: `com.apple.driver.AppleSEPManager` |
| 15.06 | `forceReset` (21:42) + `shutdown_stall` | тот же `btn_rst` |
| 16.06 | 3× `forceReset` за час | WindowServer deadlock |
До замены матрицы уже были сбои power management (SEP / wake). После ремонта доминирует другой профиль: **GUI freeze через WindowServer**, в том числе через несколько минут после загрузки.
---
## 4. Связь с заменой матрицы
Прямых строк «ошибка шлейфа» в логах нет. Косвенные признаки, согласующиеся с ремонтом дисплея:
1. **WindowServer стабильно уходит в deadlock** на всех трёх снимках
2. В стеке рядом — `corebrightnessd`, `IOMFB_bics_daemon`
3. Зависания **сразу после reboot** (2.5 и 9 мин) — типично для драйвера/железа дисплея, а не для «прогрева» одного приложения
4. Полный lockup (`btn_rst`) без kernel panic — возможен при зависании GPU/DCP-конвейера
**Вероятные аппаратные причины (для сервиса):**
- плохой контакт **шлейфа матрицы** (eDP/LVDS)
- повреждение или неправильная укладка **кабеля дисплея**
- несовместимая или дефектная **матрица** для `MacBookPro18,1` (Liquid Retina XDR)
- ошибка сборки: винты, заземление, T-Con / подсветка
---
## 5. Действия
### 5.1 Сервис (приоритет)
Вернуть в сервис с этим runbook. Попросить проверить:
1. Посадку шлейфа матрицы и display cable на обоих концах
2. Отсутствие перекоса / залома шлейфа при закрытии крышки
3. Совместимость матрицы с **MacBookPro18,1** (Liquid Retina XDR, не «похожая» панель)
4. Корректность сборки подсветки и T-Con
### 5.2 Диагностика на Mac
**Apple Diagnostics** (нативная):
1. Выключить Mac
2. Зажать питание → «Параметры» → «Диагностика»
**Safe Mode** (отсечь сторонние драйверы/агенты):
1. Выключить → зажать питание → «Продолжить с безопасным режимом»
2. Если в Safe Mode стабильно час+ — сильнее аргумент в пользу софта/агентов; если снова freeze — железо/драйвер дисплея
**Минимальная нагрузка после reboot:**
- не открывать Cursor, Brave, Telegram сразу после загрузки
- проверить, воспроизводится ли freeze на пустом рабочем столе
### 5.3 Мониторинг
```bash
# последние force reset
ls -lt /Library/Logs/DiagnosticReports/forceReset-full-* | head -5
# причина в заголовке отчёта
head -12 /Library/Logs/DiagnosticReports/forceReset-full-*.0002.diag | \
grep -o 'panic(cpu[^\\]*'
# ошибки WindowServer
log stream --predicate 'process == "WindowServer"' --level error
# перезагрузки
last reboot | head -10
```
После следующего зависания **не перезагружать сразу** (если возможно подождать 12 мин) — иногда успевает дописаться больше в `/Library/Logs/DiagnosticReports/`. На практике при hard lockup это редко помогает; основные данные уже в `forceReset-full`.
---
## 6. Краткий вывод
| Вопрос | Ответ |
|--------|-------|
| Это kernel panic? | Нет (16.06). Это **hard lockup**сброс кнопкой (`btn_rst`) |
| Сколько зафиксировано 16.06? | **3** отчёта `forceReset`; пользователь сообщал о ~4 зависаниях |
| Что зависало? | **WindowServer** — deadlock, графический стек |
| Есть ли ошибки DCP/шлейфа в логах? | **Нет** в открытом unified log |
| Главная рекомендация | **Перепроверка шлейфа и сборки дисплея в сервисе** |
| Была ли нестабильность раньше? | Да, с 1011.06 (`shutdown_stall`, SEP wake timeout) |
---
## 7. Ссылки на файлы отчётов
```
/Library/Logs/DiagnosticReports/forceReset-full-2026-06-16-213115.0002.diag
/Library/Logs/DiagnosticReports/forceReset-full-2026-06-16-213425.0002.diag
/Library/Logs/DiagnosticReports/forceReset-full-2026-06-16-214334.0002.diag
/Library/Logs/DiagnosticReports/ResetCounter-2026-06-16-213129.diag
/Library/Logs/DiagnosticReports/ResetCounter-2026-06-16-213438.diag
/Library/Logs/DiagnosticReports/ResetCounter-2026-06-16-214346.diag
/Library/Logs/DiagnosticReports/Telegram_2026-06-16-212645_Eternals.cpu_resource.diag
/Library/Logs/DiagnosticReports/deleted_2026-06-16-213726_Eternals.cpu_resource.diag
# более ранние (контекст)
/Library/Logs/DiagnosticReports/panic-full-2026-06-14-115455.0002.panic # Wake transition / SEP
/Library/Logs/DiagnosticReports/panic-full-2026-06-13-155145.0002.panic
/Library/Logs/DiagnosticReports/forceReset-full-2026-06-15-194242.0002.diag
```

42
docs/mac/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Mac — основной MacBook (Apple Silicon)
Контур для **MacBook Pro M1 Pro** (`MacBookPro18,1`): диагностика, нестабильность, дисплей.
**Проверено:** 2026-06-16
---
## Документы
| Документ | Назначение |
|----------|------------|
| **[DISPLAY_FREEZE_RUNBOOK.md](./DISPLAY_FREEZE_RUNBOOK.md)** | Зависания после замены матрицы: RCA по логам, хронология, действия |
---
## Железо (справка)
| Параметр | Значение |
|----------|----------|
| Модель | MacBook Pro 14" (`MacBookPro18,1`) |
| Чип | Apple M1 Pro, 10 ядер, 16 GB RAM |
| Дисплей | Built-in Liquid Retina XDR, 3456×2234 |
| ОС | macOS 15.3.1 (24D70) |
| Серийник | X47KCRG26W |
---
## Быстрая диагностика зависания
```bash
# последние принудительные перезагрузки (кнопка питания)
ls -lt /Library/Logs/DiagnosticReports/forceReset-full-* | head -5
# история перезагрузок
last reboot | head -10
# ошибки WindowServer в реальном времени
log stream --predicate 'process == "WindowServer"' --level error
```
Подробно: [DISPLAY_FREEZE_RUNBOOK.md](./DISPLAY_FREEZE_RUNBOOK.md)

View File

@@ -0,0 +1,237 @@
# Передача файлов Mac → Fedora (Bluetooth)
**Откуда:** этот Mac (Apple Silicon / основной)
**Куда:** MacBook Pro Intel с Fedora 43 (устройство Bluetooth: **fedora**, `60:FB:42:7D:2D:66`)
**Проверено:** 2026-06-16
Связанные документы: [README.md](./README.md), [RUNBOOK.md](./RUNBOOK.md) (WiFi BCM4322).
---
## 1. Быстрый путь (канон)
### Шаг 1 — Fedora: включить приём **до** отправки
Blueman (иконка Bluetooth в трее) → **«Принять файл»** / **Receive a File**.
Должен появиться диалог **«Ожидание входящего файла…»**.
Без этого Mac отправит файл, но на Fedora **ничего не произойдёт** — диалога не будет.
### Шаг 2 — Mac: отправить
```bash
# delivery-архив Wi-Fi fix (по умолчанию)
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh
# любой другой файл
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh /path/to/file.tar.gz
```
В окне **Bluetooth File Exchange** выбрать **fedora****Send** / **Отправить**.
### Шаг 3 — Fedora: проверить
Файл обычно в `~/Downloads` или `~/Bluetooth`:
```bash
ls -la ~/Downloads/
```
---
## 2. RCA: почему «отправилось», а на Fedora тишина
| Что видели | Причина |
|------------|---------|
| AppleScript пишет «send выполнена» | macOS **не запускает** OBEX-приём на Linux автоматически |
| Окно на Mac открылось, Fedora молчит | На Fedora **не был включён** «Принять файл» |
| `bnep0` / PAN не связан с файлами | Передача файлов — **OBEX**, не PAN |
| Устройство paired, но не Connected | На Fedora: Bluetooth on, Trust Mac, оставить в зоне видимости |
**Правило:** сначала **приём на Fedora**, потом **отправка с Mac**.
---
## 3. Предварительные условия
### Сопряжение (один раз)
**Fedora:**
```bash
bluetoothctl
power on
agent on
default-agent
scan on
# найти Mac по имени, затем:
pair <MAC_ЭТОГО_MAC>
trust <MAC_ЭТОГО_MAC>
```
**Mac:** Системные настройки → Bluetooth → подтвердить сопряжение.
### Перед каждой передачей
**Mac** — fedora в **Connected**:
```bash
system_profiler SPBluetoothDataType | grep -A6 "Connected:"
# должно быть: fedora (60:FB:42:7D:2D:66)
```
**Fedora** — Blueman установлен:
```bash
sudo dnf install blueman bluez-obexd
blueman-applet &
```
---
## 4. Подробно: Fedora (приём)
### Способ A — Blueman GUI (рекомендуется)
1. Иконка Bluetooth в трее (Blueman).
2. **Принять файл** / **Receive a File**.
3. Ждать диалог «Ожидание входящего файла…».
4. **Не закрывать** до завершения передачи.
### Способ B — скрипт-подсказка
Если скрипт уже есть на Fedora (после первой передачи или скопирован вручную):
```bash
bash prepare-receive-fedora.sh
```
Скрипт напомнит про Blueman и будет ждать файл в `~/Downloads`.
Сам по себе **не заменяет** пункт «Принять файл» в Blueman (без `obexpushd`).
### Куда попадает файл
| Среда | Путь |
|-------|------|
| GNOME + Blueman | `~/Downloads` |
| KDE | `~/Downloads` или `~/Bluetooth` |
| obexpushd | каталог из `-p` |
---
## 5. Подробно: Mac (отправка)
### Способ A — скрипт (рекомендуется)
```bash
# Wi-Fi fix archive (готовый)
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh
# произвольный файл
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh ~/Desktop/myfile.tar.gz
# другое имя BT-устройства (если не «fedora»)
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh myfile.zip my-fedora
```
Скрипт проверяет, что **fedora** в Connected, и открывает **Bluetooth File Exchange**.
### Способ B — вручную (без скрипта)
1. Finder → файл → ПКМ → **Поделиться****Bluetooth**
**или** открыть `/System/Applications/Utilities/Bluetooth File Exchange.app`
2. Перетащить файл в окно / File → Send File…
3. Выбрать **fedora****Send**
### Способ C — из терминала (то же GUI)
```bash
open -a "Bluetooth File Exchange" /path/to/file.tar.gz
```
Дальше вручную выбрать **fedora****Send**.
---
## 6. Что отправлять (WiFi fix)
Готовый архив для Fedora:
```text
docs/old_mac/deliverable/fedora-bcm4322-wifi-fix.tar.gz
```
Пересобрать:
```bash
bash ~/code/local_machine/docs/old_mac/scripts/build-delivery-archive.sh
```
После приёма на Fedora:
```bash
cd ~/Downloads
tar xzf fedora-bcm4322-wifi-fix.tar.gz
cd fedora-bcm4322-wifi-fix
sudo bash install-bcm4322-firmware-fedora.sh
sudo reboot
```
---
## 7. Диагностика
### Mac
```bash
system_profiler SPBluetoothDataType | grep -A8 "Connected:"
pgrep -lf "Bluetooth File Exchange"
```
### Fedora
```bash
bluetoothctl devices
bluetoothctl info 60:FB:42:7D:2D:66 # MAC этого Mac — подставить свой
systemctl --user status obex
journalctl -b -u bluetooth | tail -30
ls -la ~/Downloads/ ~/Bluetooth/ 2>/dev/null
```
### Типичные ошибки
| Симптом | Действие |
|---------|----------|
| Нет диалога приёма на Fedora | Blueman → **Принять файл** **до** Send на Mac |
| «fedora не в Connected» на Mac | Fedora: BT on, Trust, не спать |
| Передача оборвалась | Файл < 50 MB надёжнее; повторить с шага 1 |
| Blueman нет в трее | `blueman-applet &` |
| obex не стартует | `sudo dnf install bluez-obexd blueman` |
---
## 8. Альтернативы Bluetooth
Если BT-файлы не идут:
| Способ | Когда |
|--------|-------|
| **USB-флешка** | Самый надёжный offline |
| **USB tether** телефон → Fedora, `scp` с Mac в локалку | Если есть общая сеть |
| **Телефон как посредник** | Mac → телефон → Fedora (USB/BT) |
---
## 9. Порядок действий (шпаргалка)
```text
[FEDORA] Blueman → Принять файл → «Ожидание…»
[MAC] bash …/send-to-fedora-bluetooth.sh
[MAC] Bluetooth File Exchange → fedora → Send
[FEDORA] Файл в ~/Downloads
[FEDORA] tar xzf … && sudo bash install-….sh
```
---
*docs/old_mac — local_machine*

88
docs/old_mac/README.md Normal file
View File

@@ -0,0 +1,88 @@
# Old Mac — Fedora 43 на MacBook Pro Intel
Контур для **старого MacBook Pro (Intel)** с Fedora 43: WiFi Broadcom BCM4322, offline firmware, передача файлов с основного Mac.
---
## Документы
| Документ | Назначение |
|----------|------------|
| **[REMOTE_ACCESS.md](./REMOTE_ACCESS.md)** | **SSH и удалённое управление (хотспот / BT PAN)** |
| **[BLUETOOTH_FILE_TRANSFER.md](./BLUETOOTH_FILE_TRANSFER.md)** | Передача файлов Mac → Fedora по Bluetooth |
| [RUNBOOK.md](./RUNBOOK.md) | WiFi BCM4322: диагностика, firmware, PAN |
| [deliverable/fedora-bcm4322-wifi-fix.tar.gz](./deliverable/fedora-bcm4322-wifi-fix.tar.gz) | Архив для установки WiFi на Fedora |
---
## Удалённое управление (SSH)
Оба на хотспоте телефона; прямой ping часто блокируется **изоляцией клиентов** → обход через **Bluetooth PAN**.
```bash
# Mac (один раз)
bash ~/code/local_machine/docs/old_mac/scripts/setup-remote-mac.sh ВАШ_ЛОГИН_FEDORA
bash ~/code/local_machine/docs/old_mac/scripts/build-remote-access-archive.sh
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh \
~/code/local_machine/docs/old_mac/deliverable/fedora-remote-access.tar.gz
# Fedora (после приёма архива)
sudo bash setup-ssh-fedora.sh ВАШ_ЛОГИН
# Mac — подключение
bash ~/code/local_machine/docs/old_mac/scripts/connect-fedora.sh
```
Подробно: [REMOTE_ACCESS.md](./REMOTE_ACCESS.md)
---
**Порядок:** сначала приём на Fedora, потом отправка с Mac.
1. **Fedora:** Blueman → **«Принять файл»** → «Ожидание входящего файла…»
2. **Mac:**
```bash
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh
```
3. **Mac:** Bluetooth File Exchange → **fedora** → **Send**
Подробно: [BLUETOOTH_FILE_TRANSFER.md](./BLUETOOTH_FILE_TRANSFER.md)
---
## WiFi fix (после приёма архива)
```bash
cd ~/Downloads
tar xzf fedora-bcm4322-wifi-fix.tar.gz
cd fedora-bcm4322-wifi-fix
sudo bash install-bcm4322-firmware-fedora.sh
sudo reboot
```
---
## Скрипты (на этом Mac)
```bash
# Отправить файл (по умолчанию — Wi-Fi архив)
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh /path/to/anyfile.tar.gz
# Пересобрать delivery-архив
bash ~/code/local_machine/docs/old_mac/scripts/build-delivery-archive.sh
bash ~/code/local_machine/docs/old_mac/scripts/download-bcm4322-offline-bundle.sh
```
На Fedora перед приёмом: `scripts/prepare-receive-fedora.sh` (подсказка; Blueman обязателен).
---
## Железо
- **Fedora Mac:** MacBook Pro Intel, BCM4322, Fedora 43, ядро `6.17.1-300.fc43.x86_64`
- **BT-устройство:** fedora (`60:FB:42:7D:2D:66`)
---
*local_machine / docs/old_mac*

View File

@@ -0,0 +1,221 @@
# Удалённое управление Fedora MacBook с Mac (хотспот телефона)
**Схема:** оба ноутбука на WiFi хотспоте Redmi; прямой SSH часто **блокируется изоляцией клиентов** — обход через Bluetooth PAN.
**Проверено:** 2026-06-16 · `fedora.local``10.23.120.30`, ping с Mac **не проходит** (AP isolation).
---
## 1. Быстрый путь
### A. Один раз — Mac
```bash
bash ~/code/local_machine/docs/old_mac/scripts/setup-remote-mac.sh ВАШ_ЛОГИН_FEDORA
bash ~/code/local_machine/docs/old_mac/scripts/build-remote-access-archive.sh
bash ~/code/local_machine/docs/old_mac/scripts/send-to-fedora-bluetooth.sh \
~/code/local_machine/docs/old_mac/deliverable/fedora-remote-access.tar.gz
```
### B. Один раз — Fedora (после приёма архива по BT)
Логин: **`p`**
```bash
cd ~/Downloads
tar xzf fedora-remote-access.tar.gz
cd fedora-remote-access
sudo bash setup-ssh-fedora.sh p
sudo bash setup-bt-pan-fedora.sh
```
### C. Подключение с Mac
```bash
bash ~/code/local_machine/docs/old_mac/scripts/bootstrap-ssh-fedora.sh # один раз, копирует ключ
bash ~/code/local_machine/docs/old_mac/scripts/connect-fedora.sh # дальше без пароля
```
Пароль хранится локально: `~/.config/old_mac/fedora.env` (см. `fedora.env.example`).
### D. RDP (графический стол, xrdp :3389)
На Fedora уже может быть xrdp. С Mac:
```bash
brew install freerdp # один раз
bash ~/code/local_machine/docs/old_mac/scripts/connect-rdp-fedora.sh
```
Или **Microsoft Remote Desktop** (App Store): PC `fedora.local` или `10.23.120.30`, port **3389**, user **`p`**.
RDP и SSH одинаково **не достучатся**, пока нет L3 между Mac и Fedora (изоляция хотспота / нет BT PAN).
---
## 2. Диагностика сети хотспота
```bash
bash ~/code/local_machine/docs/old_mac/scripts/discover-fedora.sh
```
| Результат | Значение |
|-----------|----------|
| `fedora.local` резолвится | Fedora в той же WiFi сети |
| `ping: ok` | Можно SSH по LAN (`ssh fedora-mbp`) |
| `ping: FAIL` + mDNS есть | **Изоляция клиентов** на телефоне → §4 BT PAN |
| `ssh:22 open` | `connect-fedora.sh` должен работать |
Текущая типичная картина:
```text
Mac: 10.23.120.84/24
Gateway: 10.23.120.139 (телефон)
Fedora: 10.23.120.30 (fedora.local)
Ping Mac→Fedora: FAIL (изоляция)
```
---
## 3. Путь LAN (если ping проходит)
### 3.1 Отключить изоляцию на Redmi Note 14
Настройки → Режим модема / Точка доступа → **Дополнительно**:
- **AP isolation** / **Изоляция клиентов****Выкл**
- **Разрешить устройствам в сети видеть друг друга** → **Вкл** (если есть)
Переподключить WiFi на **обоих** Mac. Проверка:
```bash
ping -c2 fedora.local
bash ~/code/local_machine/docs/old_mac/scripts/connect-fedora.sh
```
### 3.2 SSH на Fedora
```bash
sudo bash setup-ssh-fedora.sh USER
```
### 3.3 SSH с Mac
```bash
ssh fedora-mbp # ~/.ssh/config
# или
bash connect-fedora.sh
```
---
## 4. Путь Bluetooth PAN (обход изоляции хотспота)
Прямой линк **Mac ↔ Fedora** поверх Bluetooth, минуя WiFi хотспота.
### 4.1 Fedora — PAN-сервер
```bash
sudo bash setup-bt-pan-fedora.sh
```
Остаётся работать в фоне (`bt-network -s nap pan0`), IP Fedora: **172.20.99.1/24**.
### 4.2 Mac — подключить PAN
1. **Bluetooth** → устройство **fedora****Connect**
(профиль «Сеть» / Network Access Point / NAP — если спросит)
2. Настроить IP:
```bash
bash ~/code/local_machine/docs/old_mac/scripts/setup-bt-pan-mac.sh
```
Mac получит **172.20.99.2/24**, ping Fedora:
```bash
ping -c2 172.20.99.1
```
### 4.3 SSH
```bash
ssh fedora-mbp-bt
# или
bash ~/code/local_machine/docs/old_mac/scripts/connect-fedora.sh
```
---
## 5. Альтернатива: Internet Sharing (Mac → Fedora)
Если PAN с ручными IP капризничает:
1. **Mac:** Системные настройки → Общий доступ → **Общий интернет**
- Раздать от: **WiFi**
- Кому: **Bluetooth PAN**
2. **Fedora:** Bluetooth → подключиться к Mac как к сети
3. Fedora получит `192.168.2.x`, Mac обычно `192.168.2.1`
4. На Fedora узнать IP: `ip -4 addr show bnep0`
5. Добавить в Mac `~/.ssh/config` HostName с этим IP или `connect-fedora.sh`
---
## 6. Скрипты
| Скрипт | Где | Назначение |
|--------|-----|------------|
| `setup-remote-mac.sh` | Mac | SSH-ключ + `~/.ssh/config` |
| `build-remote-access-archive.sh` | Mac | Архив для Fedora |
| `send-to-fedora-bluetooth.sh` | Mac | Передать архив |
| `discover-fedora.sh` | Mac | Найти Fedora, проверить LAN |
| `connect-fedora.sh` | Mac | SSH (LAN или BT) |
| `setup-ssh-fedora.sh` | Fedora | sshd + authorized_keys |
| `setup-bt-pan-fedora.sh` | Fedora | BT PAN сервер |
| `setup-bt-pan-mac.sh` | Mac | IP на bnep0 |
---
## 7. SSH config (на Mac после setup-remote-mac)
```ssh-config
Host fedora-mbp old-mac-fedora fedora
User ВАШ_ЛОГИН
HostName fedora.local
IdentityFile ~/.ssh/id_ed25519_fedora
Host fedora-mbp-bt
HostName 172.20.99.1
...
```
---
## 8. Устранение неполадок
| Проблема | Решение |
|----------|---------|
| Permission denied (publickey) | На Fedora: `setup-ssh-fedora.sh`, проверить `mac_pubkey.txt` |
| Connection refused :22 | `sudo systemctl start sshd` |
| No route to host (WiFi) | Изоляция хотспота → §4 BT PAN |
| Нет bnep0 на Mac | Fedora `setup-bt-pan-fedora.sh` + Bluetooth Connect |
| Fedora WiFi ещё без firmware | BT PAN или BT file transfer для архивов |
---
## 9. Дерево решений
```mermaid
flowchart TD
A[Нужен SSH на Fedora] --> B{ping fedora.local?}
B -->|ok| C[setup-ssh-fedora + ssh fedora-mbp]
B -->|fail| D[Изоляция хотспота]
D --> E{Можно выкл. AP isolation?}
E -->|да| C
E -->|нет| F[setup-bt-pan-fedora + setup-bt-pan-mac]
F --> G[ssh fedora-mbp-bt]
```
---
*docs/old_mac — local_machine*

116
docs/old_mac/RUNBOOK.md Normal file
View File

@@ -0,0 +1,116 @@
# Fedora 43 на MacBook Pro (Intel) — BCM4322 WiFi runbook
**Железо:** MacBook Pro Intel, Broadcom **BCM4322**, драйвер ядра **b43**
**ОС:** Fedora 43 Workstation, рабочее ядро `6.17.1-300.fc43.x86_64`
**Проверено:** 2026-06-16
**Архив для установки:** `deliverable/fedora-bcm4322-wifi-fix.tar.gz`
**Передача Mac → Fedora:** см. **[BLUETOOTH_FILE_TRANSFER.md](./BLUETOOTH_FILE_TRANSFER.md)** (порядок: приём на Fedora, затем Send с Mac).
---
## 1. Быстрый путь (WiFi fix)
```bash
cd ~/Downloads
tar xzf fedora-bcm4322-wifi-fix.tar.gz
cd fedora-bcm4322-wifi-fix
sudo bash install-bcm4322-firmware-fedora.sh
sudo reboot
nmcli device wifi list
nmcli device wifi connect "SSID" password "пароль"
```
---
## 2. RCA
| Симптом | Причина | Решение |
|---------|---------|---------|
| `Broadcom 4322 WLAN found`, WiFi нет | Драйвер b43 есть, **firmware нет** | `ucode16_mimo.fw` в `/lib/firmware/b43/` |
| `b43-fwcutter` установлен | Это утилита, не firmware | Установить `.fw` файлы |
| BT PAN: `bnep0` connect → disconnect | Профиль NM / телефон | §4.2 |
| `dnf` timeout | Нет интернета | Offline-архив или USB tether |
**Не переустанавливать Fedora** — BCM4322 снова потребует firmware.
---
## 3. Диагностика
```bash
uname -r
lspci -nn | grep -i network
lsmod | grep -E 'b43|bcma|ssb'
sudo dmesg | grep -i b43
nmcli device
ls -la /lib/firmware/b43/ucode16_mimo.fw
```
---
## 4. Интернет до установки firmware
### USB tethering (предпочтительно)
Redmi Note 14 → USB-модем → на Fedora `nmcli device``ping 8.8.8.8`.
### Bluetooth PAN
```bash
nmcli connection delete "Redmi Note 14 Network"
sudo systemctl restart bluetooth
bluetoothctl
# power on / agent on / default-agent / pair / trust / connect
```
На телефоне: Bluetooth-модем вкл., пересопрячь Fedora.
### Онлайн-путь (если интернет есть)
```bash
sudo dnf install rpmfusion-nonfree-release-tainted
sudo dnf install b43-firmware
sudo reboot
```
**Не ставить `akmod-wl`** на BCM4322.
---
## 5. После установки
```bash
sudo dmesg | grep -i b43 # нет "firmware ... not found"
nmcli device # wlan0 / wlp*
nmcli radio wifi on
nmcli device wifi list
```
Опционально (стабильность):
```bash
sudo iw dev <iface> set power_save off
```
---
## 6. Ядро
Рабочее: **6.17.1-300.fc43.x86_64**. В GRUB не выбирать 7.0.12, если загрузка нестабильна.
---
## 7. Цель
| Компонент | Состояние |
|-----------|-----------|
| Firmware | `/lib/firmware/b43/ucode16_mimo.fw` |
| Драйвер | `b43` без ошибок в dmesg |
| Интерфейс | `wlan0` / `wlp*` в nmcli |
| Сеть | WiFi connect работает |
---
*docs/old_mac — local_machine*

View File

@@ -0,0 +1,7 @@
BCM4322 b43 firmware offline bundle
Built: 2026-06-16
Source: broadcom-wl-6.30.163.46 via b43-fwcutter
Files: 155
Tarball: bcm4322-firmware-bundle.tar.gz
SHA256 tarball: 06d3f492c064c0db5549cf4e4bc5bd8fa40c15f3427ca02ec6a5d51f918b6032
SHA256 b43/ucode16_mimo.fw: 614c233b2f65653d298f74f64120068f84d031a40e3d37b68779ab7583fe574d

Some files were not shown because too many files have changed in this diff Show More