Представлен открытый проект Spotifast — проработанный и оптимизированный клиент для Spotify на Rust для ПК на Linux, macOS и Windows, который занимает 100–250 МБ ОЗУ при работе. В решении доступны плейлисты, поиск, подкасты и управление музыкой на других устройствах через Spotify Connect. Можно редактировать подборки и слушать музыку в фоне после закрытия окна. Примечательно, что есть скины от Winamp, эквалайзер и визуализации.
Команда ArtCraft выпустила 7 открытых приложений для работы с картинками, видео, векторной графикой, фотографиями, PDF, анимацией и вёрсткой для замены сервисов Adobe.
Весь набор написан с нуля на Rust для Windows, macOS и Linux. Интерфейс приложений и хоткеи сделаны максимально привычными для пользователей Adobe. При этом приложения не обязательно устанавливать — можно использовать в браузере.
Приложения от ArtCraft менее требовательны к конфигурации ПК и работают даже быстрее оригинальных приложений:
Скоро буду записывать короткие демо-ролики по функционалу CanvasDeskдля разных ролей и сделал важную вещь - дополнил первый запуск приложения выбором языка и роли, по которой дальше будет фильтроваться набор выводимых шаблонов нод и шаблонных схем.
Даже действующие 4 категории шаблонов уже начинают забивать кольцевое меню быстрого выбора и левую палитру подсказок. Что будет дальше - просто помойка, хочется этого избежать. Решил, что уже при старте нужно разделение на роли. Но оставил возможность включить все подсказки если хочется.
Я пишу небольшую утилиту datadiff на Rust. Она разбирает два файла JSON, YAML, CSV, TOML или XML и сравнивает получившиеся деревья, поэтому переставленные ключи и переформатирование ее не волнуют. Изменения она печатает путями вроде spec.replicas: 3 → 5. Долго это была отдельная команда, про которую надо было вспомнить, так что я решил встроить ее прямо в git diff. Первой версией была строчка в README: шелл-функция, которая из семи аргументов, передаваемых git внешнему драйверу, брала второй и пятый (там лежат старая и новая версии файла). Работало, пока не попался первый кривой файл.
git diff до и после datadiff
Оказалось, git считает любой ненулевой код выхода падением драйвера. Пишет fatal: external diff died и дальше ничего не показывает, все файлы после сломавшегося просто пропадают из вывода. А datadiff как нормальный CLI возвращал 1, если нашел различия, и 2 на невалидном файле. Хуже всего, что невалидный конфиг это обычное состояние: открыл YAML, начал править, запустил git diff глянуть что наделал, и получил fatal. Теперь в режиме драйвера datadiff всегда выходит с нулем, а про файл, который не смог разобрать, печатает короткую заметку и подсказку про git diff --no-ext-diff.
Внешний драйвер git вызывает только для самого git diff. git log -p, git show и git blame его игнорируют, туда можно попасть только через textconv, это фильтр, который превращает файл в текст перед обычным построчным сравнением. Я сделал для него режим normalize, он печатает файл в каноническом виде с отсортированными ключами. Если файл не разбирается, normalize отдает его как есть, и тут я накосячил: читал его через read_to_string(...).unwrap_or_default(). Файл не в UTF-8 превращался в пустую строку с обеих сторон, git видел две одинаковые пустоты и молча выкидывал файл из git log -p. Сейчас там чтение байтов и тест на это.
Самые обидные грабли нашлись не в git, а в CSV. В статье на Хабре про самодельный формат конфигов автор объяснял, зачем ему маркер «бери как есть»: чтобы 00544 не превратилось в 544. Я пошел проверять datadiff, и он делал ровно это. CSV типов не хранит, поэтому каждая ячейка, которая разбиралась как число, становилась числом, и замена 00544 на 544 считалась отсутствием изменений. Пока чинил, вылезло еще два случая. Слова, которые f64 честно принимает за число, вроде Nan и inf: человек по имени Nan превращался в NaN, а NaN не равен сам себе, так что неизменившаяся ячейка показывалась как измененная. И целые длиннее i64: они уходили во float и теряли цифры, так что два 20-значных номера счета, отличавшиеся последней цифрой, сравнивались как равные. Правило в итоге такое: ячейка становится числом, только если при этом ничего не теряется. Ведущий ноль перед цифрой, слова вроде nan и inf и слишком длинные целые оставляют ее строкой, а 100 и 100.0 по-прежнему равны. Это вошло в релиз 0.4.1.
Еще запомнился CI. После одного коммита на Windows падал actions/checkout, даже до сборки не доходило. Виноват был файл Icon\r, в нем macOS хранит кастомную иконку папки, в конце имени у него возврат каретки, и он случайно уехал в репозиторий. Windows создать такое имя не может вообще. Тесты при этом падали через раз на всех трех ОС, потому что дочерний процесс успевал завершиться раньше, чем тест дописывал ему stdin, и unwrap ловил EPIPE. Сам проект тут: https://github.com/dimanovikov/datadiff настройка для git в README занимает три строки.
Тестирую недавно вышедшую SOM (system one model) Laya для наделения CanvasDesk сверхсилами по автодополнению.
Как пользователь, я хочу чтобы проектирование в CanvasDesk было с молниеносным флоу. Я решил тестово прикрутить под это Laya - свежий open-source аналог нашумевшего облачного движка Jev от TypeSafe AI.
Почему не обычная LLM? Большие языковые модели для подсказок в реальном времени не годятся их генерация съедает от секунды до трёх. Я бы не стал ждать автогенерацию так долго. Jev и Laya - из класса System One Models: модели "быстрых интуитивных реакций". Они не разворачивают текст токен за токеном, а за один проход решают типизированные задачи: классифицируют, ранжируют варианты и выдают калиброванную вероятность.
Jev сидит в закрытом облаке по API. Laya вышла под Apache 2.0 и полностью совместима с ним по протоколу, но разворачивается локально. Размер ~421 миллионов параметров. Задержка около 33 мс на GPU и 200–450 мс на обычном офисном CPU. Не требует гонять контекст схемы через внешнюю сеть - для CanvasDesk в b2b исполнении это принципиально.
Что это даёт на практике: 1/ автодополнение формул и переменных: набираешь расчёт нагрузки, система цепляет переменные из апстрим-узлов и предлагает формулу, проверенную локальным парсером, 2/ next-node подсказки: поставил шаблон балансировщика - движок предлагает связать его с очередью и пулом воркеров, 3/ адаптация под роль: архитектору - параметры теории очередей, продакту - связки конверсий и когортного LTV.
Задача Laya - работать умным стрелочником: отбирать лучшие паттерны из каталога за доли секунды и отдавать их только при высокой уверенности. Если эксперимент взлетит, сборка схем перестанет быть укладкой асфальта вручную.
Пока заворачиваю Laya в локальный sidecar на базе python. Результаты теста и замеров скорости выкачу отдельным постом.
А пока можно самостоятельно потестить WASM версию CanvasDeskи написать в комментарий про свой опыт
Присоединяйтесь к выступлению эксперта по ИИ из MTC Web Services на открытии сезона Rust-сообщества 🤖
Привет! В Университете ИТМО завтра пройдет встреча, посвященная языку Rust, где MWS выступит партнером мероприятия.
В рамках программы Артур Казарян, разработчик в MWS AI, расскажет «Как задушить питона в ML-пайплайне» — что делать, когда для запуска инференс-пайплайнов доступен только Python. А еще покажет, как бесшовно интегрировать решение на Rust и получить хорошее ускорение.
📅 Когда: 26 сентября (суббота) с 14:00 до 18:00, Санкт-Петербург, Университет ИТМО + онлайн-трансляция
Type-driven development в Rust, часть 2/5: как доверить компилятору проверку контрактов между компонентами
Продолжаем серию о type-driven development в Rust — подходе, при котором правила предметной области выражаются в типах, а код, нарушающий эти правила, не компилируется. Рассказывает Никита Тимофеенко, разработчик команды MXDR компании F6.
В первой части типы отвечали за данные: какие значения возможны, какие комбинации допустимы, в каком порядке идут переходы. Но кроме данных в программе есть компоненты, которые общаются между собой, и у каждой такой границы есть контракт. Вторая часть посвящена тому, как записать эти контракты в типах, чтобы их нарушение не компилировалось.
После неё вы сможете найти в своём коде enum с match на каждом вызове, который на самом деле изображает открытый набор реализаций, и заменить его трейтом; отличить в контракте вход от выхода и перестать делать параметром трейта то, что должна выбирать реализация; поднять в тип размеры, которые известны ещё до запуска.
Во второй статье представлены три механизма, каждый разобран по той же схеме «проблема -> решение -> хорошие практики -> как это используют известные крейты или std библиотека»:
traits — контракт вместо конкретной реализации: код-потребитель требует поведение, а не тип, и работает с любым, кто его реализовал; новая реализация — это новый impl, а не правка общего кода;
associated types — типы, которые выбирает реализация, а не вызывающий: у каждой реализации свои типы результата и ошибки, и в один общий тип они не сводятся. Разница между параметром трейта и ассоциированным типом — это разница между входом и выходом;
const generics — значение как параметр типа: размер известен компилятору, а не хранится в рантайме, и структуры разного размера — разные типы. Что можно и чего нельзя в const-параметрах на стабильном Rust.
Отдельно рассмотрим CGP (Context-Generic Programming): что делать, когда одному типу нужно несколько реализаций одного трейта, а правило когерентности разрешает одну — и как одна и та же логика собирается под разные контексты без dyn и без match.
Примеры — из биржевой торговли, как и во всей серии, но приёмы работают в любом домене со сложными состояниями и правилами их изменения. Словарь типов продолжает часть 1.
Вторая статья «Type-driven development в Rust. Часть 2/5: задаём контракты между компонентами»уже на GitHub. Там же — компилируемые примеры ко всем приёмам, включая compile_fail-тесты на каждое «это не скомпилируется» из текста.
h5i предоставляет агентам ИИ, занимающимся программированием, безопасный форум для обмена сообщениями, позволяющий координировать работу команды, при этом каждый агент находится в своей собственной изолированной среде. Темы, ответы, утверждения, обзоры и голоса синхронизируются через Git, а возможности и учётные данные каждого агента остаются изолированными. В рамках проекта не требуется хостинг h5i и не требуется учётная запись SaaS.
Немного философии Rust, возможно кому то интересно обсудить
Уже почти 3 месяца пишу проект на Rust. Сейчас разработка стала другой, ручная работа стала роскошью, реальные задачи бизнеса быстрее решить LLM-ками. Естественно познание языка в таком режиме замедляется, хочется как-то компенсировать, хотя бы поговорить об этом.
Вот какой аспект. В Rust уровень владения/заимствования объектом - часть контракта. По большому счету можно составить примерно такую таблицу (на самом деле это дерево должно быть, но для упрощения привожу плоскую таблицу):
Просто владею объектом → T
Временно читаю чужой объект → &T
Временно изменяю чужой объект → &mut T
Один владелец, нужна куча → Box
Несколько владельцев, один поток, чтение → Rc
Нужны несколько владельцев, один поток, изменение → Rc<Cell> / Rc<RefCell>
Несколько владельцев, несколько потоков, чтение → Arc
Несколько владельцев, несколько потоков, изменение → Arc<Mutex> / Arc<RwLock>
По сути чем более строгий уровень доступа (или какой термин тут лучше?) - тем больше проверок сможет сделать компилятор. Если используете Rc - то уже потенциально возможны циклические ссылки и утечка памяти (с Box - это не возможно). Если RefCell - то компилятор не сможет проверить два borrow_mut() одновременно - будет рантайм проверка или паника.
И вот какая фишка. Часто LLM-ка дает уровень больше чем нужно. Как-то где можно обойтись ссылкой - добавит Rc<RefCell>. Т.е., по сути, код то рабочий, но лишние обертки и послабление компил-тайм проверок немного угнетают.
Но иногда бывает оправданно, когда в процессе уровень нужно ослабить, т.к. не всегда предусмотришь на этапе проектирования.
И далее возникла философская идея. А вот неплохо бы чтобы компилятор сам решал какой уровень применять. Т.е. начинаем от самого малого, если его не достаточно - то использует послабления. Если достаточно классической проверяемой ссылки - то использовать ее. Или если нужно множественное владение, но точно нет потоков - то Arc можно не использовать - достаточно Rc (если нет данных то ослабляем - берем Arc).
При этом все еще остаемся в рамках языка без GC - но вместо ручного выбора - выбор осуществляется компилятором при сборке.
Команда Cursor представила исходный код проекта minisqlite. Это реализация СУБД SQLite на языке Rust и успешно проходящая официальный набор тестов sqllogictest от проекта SQLite.
Проект minisqlite подготовлен в ходе эксперимента по проверке эффективности работы ИИ‑моделей GPT-5.5, Grok 4.5, Opus 4.8 и Fable 5, по воссозданию SQLite только на основе официального 835-страничного руководства, без доступа к оригинальному коду, интернету, наборам тестов и исполняемому файлу SQLite. Основная идея эксперимента — использование подробной спецификации в качестве промпта для создания сложного продукта.
Представленная реализация minisqlite основывается на результатах, полученных при использовании ИИ‑модели Opus 4.8.
Проект minisqlite включает около 200 тыс, строк кода на Rust, не содержащего unsafe‑блоков в коде библиотеки. Библиотека может открывать файлы, созданные в оригинальном sqlite3, и записывать файлы, которые можно прочитать в sqlite3. Реализация охватывает диалект SQL от SQLite, поддержку транзакций, 90 встроенных функций, планировщик запросов, движок выполнения запросов и движок хранения, совместимый с дисковым форматом SQLite 3. Из внешних зависимостей в minisqlite задействован только пакет elsa, с использованием которого реализован страничный кэш.
Полтора года пентестов Active Directory сводились к одному и тому же неудобству: для аудита нужен PingCastle (только Windows, только скоринг, ничего не проверяет на практике), для графа путей атаки — BloodHound плюс отдельный коллектор (SharpHound или RustHound-CE), а для реальной эксплуатации — Impacket на Python со всем шлейфом зависимостей. Три инструмента, три экосистемы, и ни один не даёт честного ответа на вопрос "эта уязвимость реально эксплуатируется в этом домене прямо сейчас, или только теоретически светится в отчёте".
Я решил закрыть это одним инструментом — adhammer.
Что внутри
adhammer — это Rust-тулкит для AD security assessment, который совмещает три слоя:
Аудит в стиле PingCastle — сканирует домен, скорит находки по severity, размечает MITRE ATT&CK тегами.
Граф путей атаки в стиле BloodHound — строит и визуализирует attack paths до Domain Admin.
Валидация с реальным PoC — вот это ключевое отличие. Guided-режим проходит по каждой находке и предлагает: "провалидировать и получить PoC?". Если да — запускает реальную атаку и помечает finding как "validated" только если получен настоящий артефакт — хеш $krb5tgs$/$krb5asrep$, реплицированный секрет krbtgt, реально выпущенный сертификат. Если атака не удалась — честно "attempted", а не тихо пропускается.
Почему Rust, а не поверх Impacket
Изначально пробовал строить поверх существующих Python-библиотек — уперся в то, что тащить весь стек зависимостей ради одного бинарника, который должен без проблем компилироваться и запускаться и с Kali, и с Windows-хоста внутри AD-сети, неудобно и хрупко.
Поэтому пришлось написать протокольный стек с нуля: DCE/RPC, NTLM, SMB2, Kerberos — практически "impacket для Rust", которого в экосистеме ещё не было. Результат — один статический бинарник без рантайм-зависимостей, кросс-компилируется под Linux и Windows.
Что дальше
Инструмент build как security research, соседствует с раскрытым в MSRC 0-day в ядре Windows. Сейчас думаю над тем, чтобы вынести протокольный стек в отдельные переиспользуемые crates — уже начал переписку с мейнтейнерами sspi-rs и RustHound-CE на предмет пересечения усилий.
Код и write-up: github.com/icedracon/adhammer
Использование — только на системах, где есть явная авторизация. SECURITY.md в репозитории описывает это подробно.
Буду рад issues, PR и просто фидбеку от тех, кто занимается AD security на практике.
Друзья, я просто обязан сказать огромное спасибо всему сообществу Хабра за вашу поддержку и активность под статьей о моем проекте Kakehashi!
Вдохновившись вашими отзывами, вчера вечером я опубликовал проект на Hacker News. Результат превзошел все ожидания: прямо сейчас тред держит 204 поинта, а репозиторий набрал более 220 звезд на GitHub.
Проект попал в радар к хардкорным системщикам со всего мира. Среди тех, кто дал звезду, оказались инженеры из команд Cursor, Fly.io, Astro, создатель пакетного менеджера Pixi, разработчик Redox OS и в дискуссии на HN был легендарный автор утилиты Cydia @saurik.
Но мне особенно приятно и дорого то, что самый первый импульс, первые звезды и конструктивный фидбэк проект получил именно здесь. Вы дали Kakehashi тот самый стартовый заряд, благодаря которому он смог громко заявить о себе на международной арене.
Огромное вам спасибо!
P.S. Хотел опубликовать в хаб «Я пиарюсь», но интерфейс не пропустил из-за нехватки кармы (нужно 30). Поэтому публикую в профильные хабы как апдейт к прошлой статье. Надеюсь на понимание!
Представлен открытый проект pdf-inspector на Rust от команды Firecrawl. Это PDF-парсер, который умеет быстро обрабатывать страницы документов. Проект преобразует содержимое в Markdown, но при этом сохраняет структуру документа и таблицы. Решение работает без ограничений, код опубликован под лицензией MIT.
Автономный цикл разведки → обнаружения → оценки → обнаружения → фаззинга → отчета → исправления для реальных ошибок, которые действительно затрагивают Rust: безопасность памяти в unsafe/FFI, DoS-атака из-за паники, вызванная недоверенными входными данными, доверие к десериализации (проверка целостности не является проверкой границ) и надежность Send/Sync + безопасность при панике. Статический анализ управляет динамическим этапом — модель угроз определяет, какой санитайзер, порог фаззинга и бюджет голосования получит каждая обнаруженная ошибка.
Детекторы: Miri (неопределенное поведение), AddressSanitizer, panic/abort, hang-timeout и cargo-fuzz для динамического воспроизведения ошибок.
Если на сцене висит ружьё то не удержался и я - запустил Rust In Peace по десятку популярных ржавых ящиков. Из тех, что в топе и парсят внешние данные, чтобы с поверхностью атаки.
На удивление балалайка не только пожужжала и съела токены, но реально нашла забавное, наделала PoC-ов и фиксов — часть уже смёржена мейнтейнерами.
По состоянию на 24.07.2026 было зарегистрировано 51 сообщение об уязвимостях в 17 независимых проектах на Rust: 12 уже исправлены и в влиты в main (включая lopdf, x509-parser, quick-xml, ntex), еще 9 приняты или находятся на рассмотрении в качестве GSHA безопасности (gitoxide, quinn-proto, rustls, ciborium, h2, hyperium).
Методически правильный подход через «модель угроз» работает иногда так же хорошо, как просто бахнуть бессистемно. Просто они находят разные баги. На одном таргете: threat-model-first взял глубину и логические баги, слепой прогон взял пачку генериков, которые модель угроз недооценила, поскольку слишком умная, но фаззинг подтвердил. Ничья, точнее дабл страйк.
Старая пословица «баги ходят косяками» работает. «Поищи ещё такие же», «в этой функции был такой-то баг — проверь остальные» — приёмы из репертуара Google Project Zero живее всех живых. RUSTSEC-патч закрыл что-то в парсере, но не тронул еще четыре точки входа — те же дырки. Добавил как третий срез.
Статика и фаззинг — это хорошо, но только PoC — мерило истины. Очевидно, но забывается. В половине случаев когда и агент-копатели, и «более умный» ревьюер независимо согласились, что путь достижим — оба ошибались, поймала это только реальная сборка и прогон против настоящего апстрима.
Слепой фаззинг хорошо но уже есть у большинства проектов, инструментированный cargo-fuzz еще лучше, а если на вход скормить находки анентской статики, то вообще бомба.
Всегда стоит почитать issues и проверить ветки перед тем как бахать репорт и репортить бахи. Вполне может приключится, что твоя находка уже закрыта. Просто не релизнута ещё. В табличке это отдельная строка «refound». Особенно обидно, было когда три баги все разом закрылись одним ещё не выпущенным архитектурным рефакторингом. А робото жужжал и хлопал в ладоши, эх.
Разглашение в open source не сильно отличается от «responsible disclosure» в закрытом софте: кто-то вливает фикс за час, кто-то говорит спасибо, кто-то наоборот - «это всё неправда». Как и в коммерции, отказ это не последняя инстанция: поставил себе таймер вернуться через месяц и перепроверить на сайлент-фиксы.
Ну и да, часть человеков роботов не любят и ругаются на «нейрослоп», несмотря на приложенный PoC и рабочий фикс. Ну штош, не привыкать. Неолуддизм и прочий подсчет букв ё, это святое
Если честно, сам не ожидал такого результата — штука реально работает, хоть токенов жрёт изрядно.
Type-driven development в Rust: делаем недопустимые состояния невыразимыми
В своей работе наша инженерная команда довольно часто сталкивается с уникальными и сложными задачами — мы решили поделиться своей экспертизой и практическими наработками. В том числе и теми задачами, которые напрямую не связанны с информационной безопасностью.
Начинаем с type-driven development в Rust — подхода, при котором правила предметной области выражаются в типах, а код, нарушающий эти правила, не компилируется. О том, как применять его на практике расскажет Никита Тимофеенко, разработчик команды MXDR компании F6.
Серия рассчитана на тех, кто уже пишет на Rust и хочет от системы типов большего, чем борьба с borrow checker-ом. Теории типов не будет — только приёмы и шаблоны для рабочего кода, почти всё на стабильном Rust.
После первой части вы сможете посмотреть на свои структуры с флагами и Option-ами, посчитать, сколько недопустимых состояний они позволяют собрать, перепроектировать их так, чтобы эти состояния перестали компилироваться, и удалить часть defensive-проверок.
В первой статье — пять техник, каждая разобрана по схеме «проблема -> решение -> хорошие практики -> как это используют известные крейты или std библиотека».
Примеры во всей серии — из биржевой торговли, но сами приёмы работают в любом домене со сложными состояниями и правилами их изменения:
newtype — свой тип для каждой роли вместо голого примитива: значения разных типов не перепутать местами, а инварианты проверяются один раз — при создании (smart constructor);
ADT — «одно из» через enum с данными в вариантах вместо булевых флагов и Option-ов, допускающих бессмысленные комбинации;
uninhabited types — типы без значений: как убрать ветку ошибки, которая «никогда не случится», так, чтобы это гарантировал компилятор, а не unreachable!();
phantom types — параметры-маркеры без рантайм-представления: одна generic-обёртка с типом-тегом вместо семейства одинаковых newtype-ов;
typestate — состояние объекта в его типе: у каждого состояния свой набор методов, переход возвращает новый тип, а неверный порядок шагов не компилируется.
В Open Source под лицензией Apache License 2.0 выложен проект Grok Build — это терминальный агент для разработки программного обеспечения на основе искусственного интеллекта от SpaceXAI.
Решение работает в виде полноэкранного интерфейса пользователя, который понимает код, редактирует файлы, выполняет команды оболочки, осуществляет поиск в интернете и управляет длительными задачами — в интерактивном режиме, в безголовом режиме для написания скриптов/CI или встроен в редакторы через протокол Agent Client Protocol (ACP).
Представлен лёгкий браузер для парсинга данных и ИИ‑агентов — открытый Obscura на Rust работает в разы быстрее подобных проектов и занимает меньше ресурсов, чем Сhrome или Firefox:
без графического интерфейса — максимальная автоматизация;
Stealth Mode, который скрывает признаки ИИ‑агентов. Так сайты не будут распознавать устройство как бота;
использует лишь 30 МБ оперативной памяти;
сам браузер весит около 70 МБ;
грузит страницы за 85 мс;
после запуска браузер готов к работе практически сразу без дополнительных настроек.