惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Palo Alto Networks Blog
Martin Fowler
Martin Fowler
T
Threatpost
D
Docker
S
Schneier on Security
M
MIT News - Artificial intelligence
G
Google Developers Blog
L
LINUX DO - 热门话题
J
Java Code Geeks
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
博客园 - Franky
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
人人都是产品经理
人人都是产品经理
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
C
CERT Recently Published Vulnerability Notes
The GitHub Blog
The GitHub Blog
Simon Willison's Weblog
Simon Willison's Weblog
NISL@THU
NISL@THU
MongoDB | Blog
MongoDB | Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Heimdal Security Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
SecWiki News
SecWiki News
P
Privacy International News Feed
P
Proofpoint News Feed
O
OpenAI News
B
Blog
腾讯CDC
F
Full Disclosure
Apple Machine Learning Research
Apple Machine Learning Research
T
Tor Project blog
H
Hacker News: Front Page
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
C
Cisco Blogs
S
Security Affairs

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как запустить VLESS + Reality на старом Intel iMac с macOS Catalina 10.15.8
OKTHEOK · 2026-04-24 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение9 мин

Охват и читатели208

Туториал

Актуальные proxy-клиенты на старых Mac часто ведут себя одинаково: если устанавливаются, импортируют профиль, и даже показывают список серверов, не доводят дело до рабочего подключения. На практике это особенно заметно на macOS Catalina 10.15.8. Текущий Happ для Mac официально требует macOS 12.0 или новее, а значит для Catalina уже не подходит как штатный путь. IRBox в релизах заявляет поддержку macOS 10.15+, но в моём тесте на Catalina встроенный core упёрся в несовместимость с системой. Рабочий результат в итоге дал V2RayXS 1.5.9 для x86_64: не через обычный импорт подписки, а через ручную сборку VLESS + Reality outbound.

Дисклеймер
Я не сетевой инженер и не специалист по обходу блокировок. Я написал эту инструкцию как пользователь для таких же пользователей, которые столкнулись с задачей, и не смогли найти в одном месте понятную пошаговую инструкцию. Для тех, кто просто хочет последовательно добиться рабочего результата на старом Mac.

Эта статья рассчитана на ситуацию, когда у вас старый Intel Mac, macOS Catalina 10.15.x, подписка с VLESS-узлами и обычный импорт подписки в клиенте либо не работает, либо создаёт пустой конфиг.

macOS Catalina 10.15.8 на Intel iMac 2013 — исходная система, на которой проводилась настройка

macOS Catalina 10.15.8 на Intel iMac 2013 — исходная система, на которой проводилась настройка

Как определить, какая версия V2RayXS подходит для Catalina

Это важный шаг, который легко пропустить. На старом macOS нельзя просто скачать «самую новую» версию клиента и ожидать, что она будет работать.

Логика выбора для Intel Mac с Catalina 10.15.x была такой:

  1. сначала нужно смотреть не только на название приложения, но и на архитектуру сборки — в нашем случае нужна именно x86_64, а не arm64;

  2. затем нужно проверить release notes старых версий на странице релизов V2RayXS;

  3. если в релизе разработчик сам предупреждает о проблеме именно в x86_64-сборке, такую версию для Catalina лучше не брать;

  4. если в более раннем релизе есть правка, связанная с realitySettings или импортом VLESS, такая версия становится приоритетной.

В моём случае это привело к такому выводу:

  • V2RayXS 1.5.9 x86_64 — лучший кандидат для Catalina;

  • V2RayXS 1.5.10 x86_64 — нежелателен, потому что сам разработчик предупреждает, что в этой сборке встроен неправильный bundled xray core package;

  • более новые версии на старом Intel Mac стоит проверять только после явного изучения release notes.

Практическое правило простое: для Catalina сначала искать последнюю стабильную x86_64-сборку до проблемного релиза, а уже потом экспериментировать с более новыми.

Где скачивать старые версии V2RayXS

Искать старые версии лучше в двух местах:

1. Официальные GitHub Releases

Основной источник — страница релизов проекта V2RayXS. Там можно проверить историю версий, changelog и названия файлов.

Релизы V2RayXS: для Catalina важна именно x86_64-сборка и примечания к конкретной версии

Релизы V2RayXS: для Catalina важна именно x86_64-сборка и примечания к конкретной версии

2. Exact mirror на SourceForge

Для старых сборок V2RayXS есть удобное зеркало на SourceForge. Это полезно, когда GitHub-asset неудобно скачивать напрямую или нужна конкретная старая версия.

Важный нюанс: старые версии V2RayXS обычно распространяются не как DMG, а как ZIP-архив с .app внутри. То есть для Catalina не стоит искать именно DMG любой ценой: для V2RayXS рабочий формат старых релизов — это чаще V2RayXS_x86_64.app.zip.

Схема загрузки такая:

  1. открыть релизы проекта;

  2. найти нужную версию;

  3. выбрать x86_64-сборку;

  4. скачать ZIP-архив;

  5. распаковать его и перенести .app в Applications.

Старая x86_64-версия V2RayXS для Catalina распространяется как архив приложения, а не как DMG

Старая x86_64-версия V2RayXS для Catalina распространяется как архив приложения, а не как DMG

Что не сработало

Happ

Как основной клиент для Catalina его лучше не брать: текущая версия в Mac App Store требует macOS 12.0 or later. То есть для Catalina этот путь закрыт официально.

IRBox

В релизах IRBox прямо указаны сборки для macOS Intel и Apple Silicon, а в описании релиза сказано, что приложение включает sing-box и xray-core. Формально это выглядит перспективно для Catalina. Но в реальном тесте на 10.15.8 bundled core может оказаться несовместимым с системой.

IRBox на Catalina формально запускается, но на практике упирается в несовместимость встроенного core

IRBox на Catalina формально запускается, но на практике упирается в несовместимость встроенного core

Что в итоге сработало

Рабочая схема выглядела так:

  1. отсеять клиенты, которые не подходят по macOS или реально не запускаются;

  2. установить V2RayXS 1.5.9 x86_64;

  3. не полагаться на auto-import подписки, если он создаёт пустой config.json;

  4. локально декодировать подписку на самом Mac;

  5. собрать отдельный JSON-outbound для каждого VLESS/Reality-узла;

  6. добавить нужные узлы через Advanced -> Outbounds;

  7. загрузить core и включить Global Mode;

  8. отдельно настроить Telegram Desktop на локальный SOCKS5.

V2RayXS — это GUI для xray-core на macOS. Приложение поднимает локальный SOCKS5 на 127.0.0.1:1081 и HTTP proxy на 127.0.0.1:8001, а также работает в режимах Global Mode, PAC Mode и Manual Mode.

V2RayXS на Catalina: ключевые режимы работы и управление загрузкой core

V2RayXS на Catalina: ключевые режимы работы и управление загрузкой core

Почему обычный импорт подписки может не сработать

У V2RayXS на Catalina может возникнуть типичная проблема: подписка подтягивается, пункты в меню появляются, но итоговый config.json содержит не реальный узел, а пустую заготовку, например:

  • address: ""

  • port: 0

  • нулевой UUID

  • security: "none"

  • пустой realitySettings

Если вы видите такую картину, значит клиент не собрал полноценный outbound из подписки, и дальше нужно идти через ручной JSON.

"outbounds": [
  {
    "protocol": "vless",
    "protocol": "vless",
    "settings": {
      "vnext": [
        {
          "address": "",
          "users": [
            {
              "flow": "",
              "id": "00000000-0000-0000-0000-000000000000",
              "encryption": "none"
            }
          ],
          "port": 0
        }
      ]
    },
    "tag": "VLESS",
    "streamSettings": {
      "security": "none",
      "realitySettings": {},
      "network": "tcp"
    }
  }
]

Признаки сломанного импорта: пустой address, port=0, нулевой UUID, security=none и пустой realitySettings.

Как декодировать подписку локально, не публикуя её

Если у вас есть только subscription URL, не публикуйте его и не отправляйте в онлайн-конвертеры. Всё можно сделать локально на самом Mac.

Откройте Terminal и выполните:

curl -L '<SUBSCRIPTION_URL>' | tr -d '\r\n' | base64 -D | nl -ba > ~/Desktop/vless-list.txt
open ~/Desktop/vless-list.txt

Что делает эта команда:

  • скачивает подписку;

  • декодирует base64;

  • нумерует все строки;

  • сохраняет их в ~/Desktop/vless-list.txt.

В итоге вы получаете локальный список всех vless://...-ссылок без публикации URL и без передачи содержимого на сторонние сервисы.

Подписка декодируется локально на Mac: список VLESS-узлов сохраняется в vless-list.txt без использования сторонних сервисов

Подписка декодируется локально на Mac: список VLESS-узлов сохраняется в vless-list.txt без использования сторонних сервисов

Как выглядит рабочий VLESS + Reality узел

Если взять один такой vless://...-линк и разобрать его на части, в нём обычно есть:

  • адрес сервера и порт;

  • UUID;

  • security=reality;

  • flow=xtls-rprx-vision;

  • sni, pbk, sid, fp, иногда spx.

Это нужно превратить в JSON-outbound для Xray. Базовый шаблон выглядит так:

{
  "protocol": "vless",
  "tag": "node-001",
  "settings": {
    "vnext": [
      {
        "address": "example.com",
        "port": 443,
        "users": [
          {
            "id": "UUID-HERE",
            "encryption": "none",
            "flow": "xtls-rprx-vision"
          }
        ]
      }
    ]
  },
  "streamSettings": {
    "network": "tcp",
    "security": "reality",
    "realitySettings": {
      "serverName": "SNI-HERE",
      "fingerprint": "firefox",
      "publicKey": "PUBLIC-KEY-HERE",
      "shortId": "SHORTID-HERE",
      "spiderX": "/PATH-HERE"
    }
  }
}

Важно: tag должен быть уникальным. Не используйте main: в V2RayXS это служебный тег.

Как добавить узел вручную в V2RayXS

Откройте Configure... -> Advanced... -> Outbounds.

Дальше:

  1. нажмите +;

  2. вставьте JSON одного узла;

  3. нажмите Finish;

  4. вернитесь в основное окно Configure...;

  5. обязательно нажмите OK.

Ручное добавление VLESS + Reality узла через Advanced -> Outbounds

Ручное добавление VLESS + Reality узла через Advanced -> Outbounds

Если после Finish вы не нажмёте OK в основном окне Configure, правка может не сохраниться.

После сохранения в Configure... должны появиться реальные параметры узла, а не пустая заглушка

После сохранения в Configure... должны появиться реальные параметры узла, а не пустая заглушка

Как проверить, что конфиг действительно рабочий

Нажмите Load core, затем откройте View current config.json....

После этого в итоговом config.json должно быть видно:

  • routing.rules[].outboundTag указывает на ваш тег;

  • в outbounds указан реальный address;

  • указан реальный port;

  • streamSettings.security равно "reality";

  • realitySettings заполнен.

Если всё это есть, значит вы получили не пустой шаблон, а настоящий рабочий Xray-конфиг.

"outbounds": [
  {
    "protocol": "vless",
    "settings": {
      "vnext": [
        {
          "address": "example-server.com",
          "users": [
            {
              "flow": "xtls-rprx-vision",
              "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
              "encryption": "none"
            }
          ],
          "port": 443
        }
      ]
    },
    "tag": "reality 1",
    "streamSettings": {
      "network": "tcp",
      "realitySettings": {
        "publicKey": "PUBLIC_KEY_HERE",
        "serverName": "example-sni.com",
        "shortId": "SHORT_ID_HERE",
        "spiderX": "/example-path",
        "fingerprint": "firefox"
      },
      "security": "reality"
    }
  }
]

Признак правильного импорта: заполнены address, port, UUID, flow, security=reality и блок realitySettings

Как включить подключение

Когда ручной узел уже сохранён и виден в config.json:

  1. выберите нужный сервер в меню V2RayXS;

  2. нажмите Load core;

  3. затем включите Global Mode.

Если интернет заработал — схема рабочая.

Если интернет пропал:

  • вернитесь в Manual Mode;

  • проверьте config.json;

  • убедитесь, что выбран именно ваш ручной узел, а не пустой шаблон.

Рабочее состояние: core загружен, выбран ручной узел, включён Global Mode

Рабочее состояние: core загружен, выбран ручной узел, включён Global Mode

Почему браузер работает, а Telegram Desktop — нет

Даже если в браузере уже открываются и обычные, и заблокированные сайты, Telegram Desktop может не заработать автоматически. В таком случае проблема обычно не в VLESS и не в сервере, а в том, что Telegram не использует системный proxy V2RayXS так же, как браузер.

Решение:

  • тип proxy: SOCKS5;

  • host: 127.0.0.1;

  • port: 1081;

  • логин и пароль: пусто.

После этого Telegram начинает работать через локальный SOCKS5 V2RayXS.

Если Telegram не подхватывает системный proxy, его нужно вручную направить на локальный SOCKS5 V2RayXS

Если Telegram не подхватывает системный proxy, его нужно вручную направить на локальный SOCKS5 V2RayXS

Почему скорость может быть ниже, чем на другом Mac

Если подключение уже работает, но скорость заметно ниже, это не обязательно ошибка настройки. На старом iMac типичные причины такие:

  • выбран перегруженный узел;

  • более слабый Intel CPU;

  • iMac сидит в диапазоне 2.4 GHz, а не 5 GHz;

  • на другом компьютере используется другой клиент или другой сетевой маршрут.

На практике переход на 5 GHz может дать очень заметный прирост скорости.

Как подготовить JSON для всех VLESS-узлов, не публикуя подписку

Если узлов много, вручную собирать JSON для каждого неудобно. Ниже — локальный Python-скрипт, который:

  • скачивает подписку по URL;

  • декодирует её;

  • находит все vless://...-ссылки;

  • превращает каждый узел в отдельный JSON-файл для V2RayXS;

  • ничего не отправляет на сторонние сервисы.

Сохраните файл как ~/Desktop/convert_subscription.py:

#!/usr/bin/env python3
import base64
import json
import os
import re
import sys
import urllib.request
from urllib.parse import urlparse, parse_qs, unquote

def safe_name(s: str) -> str:
    s = unquote(s).strip()
    s = re.sub(r'[\\/:*?"<>|]+', "_", s)
    s = re.sub(r"\s+", " ", s)
    return s[:80] if s else "node"

def first(params, key, default=""):
    values = params.get(key, [])
    return values[0] if values else default

def vless_to_outbound(link: str, index: int) -> dict:
    p = urlparse(link.strip())
    if p.scheme.lower() != "vless":
        raise ValueError("Not a vless link")

    params = parse_qs(p.query)
    tag = f"node-{index:03d} {safe_name(p.fragment)}".strip()

    user = {
        "id": p.username or "",
        "encryption": first(params, "encryption", "none"),
    }
    flow = first(params, "flow", "")
    if flow:
        user["flow"] = flow

    outbound = {
        "protocol": "vless",
        "tag": tag,
        "settings": {
            "vnext": [
                {
                    "address": p.hostname or "",
                    "port": p.port or 443,
                    "users": [user]
                }
            ]
        },
        "streamSettings": {
            "network": first(params, "type", "tcp"),
            "security": first(params, "security", "none")
        }
    }

    if outbound["streamSettings"]["security"] == "reality":
        reality = {
            "serverName": first(params, "sni", ""),
            "fingerprint": first(params, "fp", ""),
            "publicKey": first(params, "pbk", ""),
            "shortId": first(params, "sid", ""),
            "spiderX": unquote(first(params, "spx", "")),
        }
        reality = {k: v for k, v in reality.items() if v}
        outbound["streamSettings"]["realitySettings"] = reality

    return outbound

def main():
    if len(sys.argv) != 2:
        print("Usage: python3 convert_subscription.py '<SUBSCRIPTION_URL>'")
        sys.exit(1)

    sub_url = sys.argv[1]
    with urllib.request.urlopen(sub_url) as r:
        raw = r.read().decode("utf-8", errors="ignore").strip()

    decoded = base64.b64decode(raw).decode("utf-8", errors="ignore")
    links = [line.strip() for line in decoded.splitlines() if line.strip().lower().startswith("vless://")]

    if not links:
        print("No VLESS links found.")
        sys.exit(2)

    out_dir = os.path.expanduser("~/Desktop/v2rayxs-outbounds")
    os.makedirs(out_dir, exist_ok=True)

    index_lines = []
    for i, link in enumerate(links, start=1):
        outbound = vless_to_outbound(link, i)
        filename = f"{i:03d} - {safe_name(outbound['tag'])}.json"
        filepath = os.path.join(out_dir, filename)
        with open(filepath, "w", encoding="utf-8") as f:
            json.dump(outbound, f, ensure_ascii=False, indent=2)
        index_lines.append(f"{i:03d} -> {outbound['tag']}")

    with open(os.path.join(out_dir, "INDEX.txt"), "w", encoding="utf-8") as f:
        f.write("\n".join(index_lines))

    print(f"Done. Files saved to: {out_dir}")

if __name__ == "__main__":
    main()

Запуск:

python3 ~/Desktop/convert_subscription.py '<SUBSCRIPTION_URL>'
open ~/Desktop/v2rayxs-outbounds

После этого на рабочем столе появится папка v2rayxs-outbounds с JSON-файлами.

Скрипт раскладывает подписку на отдельные JSON-файлы.

Каждый такой файл можно открывать, копировать код и вставлять в Advanced -> Outbounds

Что сработало

Сработало:

  • отказаться от Happ как от основного сценария для Catalina;

  • не переоценивать формальную совместимость IRBox с macOS 10.15;

  • использовать V2RayXS как GUI над Xray;

  • не доверять auto-import, если он создаёт пустой конфиг;

  • собирать VLESS + Reality вручную через Advanced -> Outbounds;

  • подключить Telegram Desktop отдельно через SOCKS5 127.0.0.1:1081;

  • проверить диапазон Wi-Fi и перейти на 5 GHz.

Что не сработало

Не сработало:

  • ожидание, что «подписка импортировалась — значит всё готово»;

  • попытка работать с пустыми VLESS-пунктами из кривого импорта;

  • надежда, что браузер и Telegram поведут себя одинаково;

  • попытка использовать неподходящий клиент просто потому, что он хорошо работает на других устройствах.

Вывод

Главный вывод из этой истории простой: на старом Intel Mac с macOS Catalina рабочее подключение зависит не столько от формальной поддержки протокола VLESS/Reality, сколько от того, насколько конкретный клиент и его bundled core реально совместимы с системой. Happ сейчас требует macOS 12+ и для Catalina отпадает официально. IRBox формально поддерживает macOS 10.15+, но на практике оказалось что это не так. А V2RayXS оказался рабочим именно потому, что позволил обойти кривой импорт и вручную собрать нормальный Xray-конфиг.