Я давно пользуюсь Claude Desktop и Claude Code на Windows. В какой-то момент стало интересно: когда я выхожу из аккаунта и захожу заново, насколько «с чистого листа» меня видит сервер? Оказалось, что аккаунт — это только половина истории. Claude помечает ещё и саму машину, причём отдельно от логина, и эти метки живут на диске между сессиями.
В статье разберу, где именно лежат идентификаторы устройства, как клиент их генерирует (с кусками кода из app.asar и бинаря CLI), что из этого переживает наивное «удалить папку», и покажу PowerShell-скрипт, который приводит локальный слой в состояние первого запуска. Чаты при этом остаются на месте.
Весь код открыт (скрипт для сброса идентификаторов Claude Code): github.com/Soulringen/aegis-claude
Дисклеймер. Это исследование того, что клиент пишет на диск вашего же компьютера, и утилита для управления своими локальными данными. Никакого вмешательства в чужие системы или серверную часть здесь нет.
С чего всё началось
Первое, что бросается в глаза — файл ~/.claude.json (на Windows это %USERPROFILE%\.claude.json). Внутри, помимо кэшей фич и списка проектов, лежат два долгоживущих идентификатора:
{ "userID": "29751f55233dc772e0f88584ffbd5317d59c84d782caf321b2ad408cc237bfb1", "machineID": "c6604aae3f882f340123383c64ddf99644b7d2f060177351910f4de23ecedddb", "firstStartTime": "2026-06-10T21:27:47.643Z", "oauthAccount": { "accountUuid": "…", "emailAddress": "…", "organizationUuid": "…" } }
machineID и userID — это 32 случайных байта в hex (отсюда длина 64 символа). Важный момент: они не привязаны к аккаунту. То есть под какой учёткой бы вы ни зашли на этой машине, пара ID остаётся прежней. Это ровно то, что позволяет связать разные аккаунты, работавшие на одном ПК.
Чтобы убедиться, что это не просто кэш, я вытащил логику генерации прямо из бинаря CLI (claude.exe, это упакованный Bun-проект). После деминификации она выглядит так:
function getUserID(e) { let n = oe(), r = ce(); if (typeof r.userID === "string" && qc.test(r.userID)) return r.userID; if (n.generatedUserID) return n.generatedUserID; let s = Dc(32).toString("hex"); // 32 случайных байта -> hex return n.setGeneratedUserID(s), Ce((g) => ({ ...g, userID: s }), e), s; } function getMachineID(e) { let n = oe(), r = ce(); if (typeof r.machineID === "string" && qc.test(r.machineID)) return r.machineID; if (n.generatedMachineID) return n.generatedMachineID; let s = Dc(32).toString("hex"); return n.setGeneratedMachineID(s), Ce((g) => ({ ...g, machineID: s }), e), s; }
Логика простая: есть валидное значение в конфиге — используем его; нет — генерируем новое и сохраняем. Значит, достаточно убрать значение из конфига, и при следующем запуске появится новый ID. Там же рядом нашёлся summonSidKey и, что интереснее, chromeExtension.pairedDeviceId — привязка к расширению Claude для Chrome.
Отдельная подстава: CLI периодически сохраняет резервные копии конфига в ~/.claude\backups\.claude.json.backup.*. Если удалить только основной файл, клиент может поднять старый machineID из бэкапа. Это нужно учитывать при чистке.
Десктоп: ant-did и реестр устройств
У десктопного приложения своя система меток в %APPDATA%\Claude\. Центральный файл — ant-did:
var $Ht = "ant-did"; function readAntDid(e) { let t = Buffer.from(e, "base64").toString("utf-8").trim(); return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t) ? t : void 0; } function ensureAntDid() { let e = path.resolve(app.getPath("userData"), $Ht); let t = readFile(e); if (t !== void 0) return t; let n = fs.lstatSync(e); if (!n.isFile() || n.nlink > 1) throw Error("ant-did is not a plain file"); // … let id = randomUUID(); writeFileSync(e, Buffer.from(id, "utf-8").toString("base64"), { mode: 384, flag: "wx" }); return id; }
Это случайный UUID, записанный в base64. Он один на OS-аккаунт и подставляется во все события телеметрии. Что характерно, это ровно тот же did, который лежит в %APPDATA%\Claude\sentry\session.json — то есть крэш-репорты Sentry используют тот же идентификатор устройства.
Рядом — ant-device-registry.json. Он привязывает машину к каждому аккаунту:
{ "17132d96-…": "pk1:<ключ_устройства>:cf3c2a51-…", "acaed4ef-…": "pk1:<ключ_устройства>:8379cfe6-…" }
Ключами идут UUID аккаунтов, значениями — записи pk1:… с ключом устройства. У меня в этом файле оказалось две учётки с общим ключом устройства. Это уже серверная связка: даже если вычистить всё локально, сервер помнит, что эти аккаунты регистрировали одно и то же устройство.
Дальше по мелочи, но каждое — стабильный идентификатор или соль:
remote-control-state.json→telemetrySalt(соль телеметрии, мятится черезmintSalt());ccd-ids.json→salt(ещё одна соль);config.json→first_launch_at,lastKnownAccountUuid, кэш токеновoauth:tokenCache;claude_desktop_config.json→remoteToolsDeviceName(имя устройства, по сути hostname) и карты настроек по аккаунтам.
Chromium-слой, о котором все забывают
Десктоп Claude — это Electron, поэтому внутри живёт полноценный Chromium-профиль. И вот о нём в большинстве «гайдов по сбросу» вообще не вспоминают. В %APPDATA%\Claude\ лежат:
Network\Cookies— cookiesclaude.ai, включаяajs_anonymous_id,anthropic-device-id,activitySessionId;Local Storage\leveldb,IndexedDB,Session Storage— веб-хранилище;Preferences— там, например,spellcheck.dictionaries: ["ru"]иmedia.device_id_salt;Local State—os_crypt.encrypted_keyиuninstall_metrics.installation_date2.
Связь аналитики с устройством видна прямо в коде десктопа:
e.SEGMENT_ANONYMOUS_ID = "ajs_anonymous_id"; e.DEVICE_ID_KEY = "anthropic-device-id"; // … event_data: { device_id: await get("anthropic-device-id"), anonymous_id: await get("ajs_anonymous_id"), // … }
То есть ajs_anonymous_id + anthropic-device-id уходят в события GrowthBook/аналитики. Причём один и тот же ajs_anonymous_id я нашёл и в Local Storage обычного Chrome (там, где открывал claude.ai в браузере) — а это уже совсем другой профиль, который десктопный сброс не трогает.
Ещё одно неочевидное место — %TEMP%\claude\cache-break-state-*.json: по файлу на сессию, в имени — UUID. И каталог %LOCALAPPDATA%\ClaudeDesktopRollbackBackups\, где после обновлений остаётся старая копия ant-did и всего Roaming\Claude. Снесёте текущий — всё поднимется из отката.
Что переживает наивное «удалить папку»
Это ключевой вывод исследования. Даже если идеально вычистить все файлы выше, устройство всё равно опознаётся по тому, что к файлам не относится:
Канал |
Что утекает |
Лечится удалением файлов? |
|---|---|---|
Локаль ОС |
ru-RU, раскладка 1049 |
нет |
Часовой пояс |
Russian Standard Time (UTC+3) |
нет |
Spellcheck |
dictionaries: ["ru"] |
частично |
IP / TLS / HTTP2 |
гео, ASN, JA3 |
нет |
OAuth-связка |
accountUuid уже склеен с pk1 на сервере |
нет |
Браузер |
cookies claude.ai в обычном Chrome/Edge |
нет (другой профиль) |
Вывод простой: локальный сброс идентификаторов — это один слой из нескольких. Он нужен, чтобы новый вход не садился на старый machineID/ant-did, но сам по себе он не делает вас «новым пользователем» в глазах сервера. Поэтому в утилите я сознательно не обещаю «обход бана» — она делает ровно одно: приводит локальные метки устройства в состояние первого запуска.
Скрипт
Собрал всё в один PowerShell-скрипт (PowerShell 5.1 уже есть в любой Windows 10/11, зависимостей нет, права администратора не нужны — всё в профиле пользователя).
Ключевые решения:
По умолчанию — предпросмотр. Скрипт сначала показывает, что нашёл, и не удаляет ничего, пока вы явно не выберете 1.
Mode: PREVIEW (deletion starts only after you choose 1) Current markers: CLI machineID : c6604aae...dddb CLI userID : 29751f55...bfb1 ant-did : MGU5Y2E2...M2M2 device registry entries: 2 telemetrySalt : 2023f2d5...5a4f
Чаты не трогаются. Диалоги Claude Code (~/.claude\projects), сессии и settings.json выводятся отдельным блоком «Kept» и остаются на диске.
Сначала гасим процессы. Пока жив Claude.exe, leveldb и cookies залочены. Скрипт останавливает процессы Claude и cowork-службу, ждёт, и только потом удаляет, с повторной попыткой для занятых файлов.
Защита от junction/symlink. Перед rmdir /s дерево проверяется на reparse-point, чтобы удаление не ушло по ссылке наружу:
function Test-TreeHasReparsePoint { param([string]$Path) $queue = New-Object System.Collections.Generic.Queue[string] $queue.Enqueue($Path) while ($queue.Count -gt 0) { $current = $queue.Dequeue() foreach ($child in @(Get-ChildItem -LiteralPath $current -Force -ErrorAction SilentlyContinue)) { if ($child.Attributes -band [IO.FileAttributes]::ReparsePoint) { return $true } if ($child.PSIsContainer) { $queue.Enqueue($child.FullName) } } } return $false }
До и после. В конце скрипт повторно печатает маркеры — видно, что machineID, ant-did и остальное стало absent.
Удаляются: .claude.json и его бэкапы, .credentials.json, ant-did, ant-device-registry.json, соли телеметрии, config.json, весь Chromium-слой десктопа, cache-break-state-*.json, деревья Claude-3p, claude-cli-nodejs и ClaudeDesktopRollbackBackups. Остаётся: само приложение, чаты, настройки и MCP-конфиг (из него лишь вычищаются имя устройства и карты по аккаунтам).
Как запустить
Скачать ZIP с GitHub и распаковать.
Закрыть Claude.
Двойной клик по
Reset-ClaudeIdentity.bat.В окне нажать
1— удалить, или2— выйти без изменений.
Лаунчер — тонкая обёртка, которая запускает скрипт в обход ExecutionPolicy и держит окно открытым, если PowerShell не смог стартовать:
@echo off setlocal cd /d "%~dp0" "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -File "%~dp0Reset-ClaudeIdentity.ps1" set "EC=%ERRORLEVEL%" if "%EC%"=="0" goto end if "%EC%"=="2" goto end echo. echo PowerShell could not run the script (code %EC%). pause :end endlocal
Репозиторий со скриптом, лаунчером и README (на русском и английском): github.com/Soulringen/aegis-claude
Итог
Claude помечает не только аккаунт, но и машину, и делает это в полудюжине мест: CLI-конфиг, ant-did, реестр устройств, соли телеметрии, Chromium-хранилище, временные файлы и даже откатные бэкапы. Разобраться в этом было интереснее, чем писать сам скрипт. А главный практический вывод — что локаль, часовой пояс, IP и серверная связка аккаунта остаются вне досягаемости любого локального клинера, и это стоит держать в голове.
Если тема близка — обсуждаем в чате и ловим ресеты Claude/Codex на канале (ссылки в репозитории). Буду рад замечаниям по коду и местам, которые я мог пропустить.