[Эпик 7] Мост подписи CoopID: signChainDigest + WalletPluginCoopId + PIN-кэш + CoopID-сессия #145

Closed
claude wants to merge 4 commits from feat/coopid-7-signing-bridge into feat/coopid-12-recovery-account-from-confirm
Owner

Мост подписи CoopID (Эпик 7)

Десктоп подписывает он-чейн транзакции ключом пайщика из RAM-keystore @coopenomics/auth, не извлекая приватный ключ — как Ledger/Anchor-плагины. Заменяет путь WalletPluginPrivateKey(globalStore.wif) на подпись «чужими руками».

Коммиты

  1. SDK signChainDigest (@coopenomics/auth) — keystore подписывает signing-дайджест транзакции, наружу только SIG_K1_…; ключ через пакет-внутреннюю readUnlockedKey(). Тесты signing.test.ts 16/16 (recoverDigest даёт pubkey подписанта + integrity + WalletLocked).
  2. PIN-кэш ключа (воскрешён pin.ts, снят в 11.8) — уточнённая at-rest модель (супердит «RAM-only» 11.8): пароль только для входа/расшифровки серверного блоба; после входа WIF re-шифруется ПИНом в локальный кэш; re-unlock (reload/авто-лок) — ПИНом, не паролём; дефолт 000000 прозрачно; авто-лок 30 мин. persistPinCache/unlockWithPin/hasPinCache/clearPinCache. Тесты pin.test.ts 7/7.
  3. Desktop-мостWalletPluginCoopId (делегирует в signChainDigest) + ветка CoopID-сессии в session.init (строго аддитивно, легаси-путь не тронут): establishCoopIdSession строит wharfkit Session поверх keystore; ensureWalletUnlocked — единая точка авто-unlock по PIN-кэшу перед подписью; авто-лок RAM 30 мин; StorageAdapter поверх IndexedDB.

Проверки

  • @coopenomics/auth: vitest pin 7/7 + signing 16/16 + wallet/regression зелёные; tsc --noEmit EXIT 0; ESLint 0; unbuild OK.
  • desktop: ESLint 0 по всем изменённым файлам. Полный vue-tsc — в CI.

Известный гейт (вынесен в задачу #35)

Токены CoopID (oidc/tokens.ts) — RAM-only: CoopID-сессия не переживает F5 (ключ поднимется из PIN-кэша, токен потеряется). Нужна персистентность токенов (зеркало легаси) до подключения recovery как потребителя с reload-survival.

🤖 Generated with Claude Code

## Мост подписи CoopID (Эпик 7) Десктоп подписывает он-чейн транзакции ключом пайщика из RAM-keystore `@coopenomics/auth`, **не извлекая приватный ключ** — как Ledger/Anchor-плагины. Заменяет путь `WalletPluginPrivateKey(globalStore.wif)` на подпись «чужими руками». ### Коммиты 1. **SDK `signChainDigest`** (`@coopenomics/auth`) — keystore подписывает signing-дайджест транзакции, наружу только `SIG_K1_…`; ключ через пакет-внутреннюю `readUnlockedKey()`. Тесты `signing.test.ts` 16/16 (`recoverDigest` даёт pubkey подписанта + integrity + WalletLocked). 2. **PIN-кэш ключа** (воскрешён `pin.ts`, снят в 11.8) — уточнённая at-rest модель (**супердит «RAM-only» 11.8**): пароль только для входа/расшифровки серверного блоба; после входа WIF re-шифруется ПИНом в локальный кэш; re-unlock (reload/авто-лок) — ПИНом, не паролём; дефолт `000000` прозрачно; авто-лок 30 мин. `persistPinCache`/`unlockWithPin`/`hasPinCache`/`clearPinCache`. Тесты `pin.test.ts` 7/7. 3. **Desktop-мост** — `WalletPluginCoopId` (делегирует в `signChainDigest`) + ветка CoopID-сессии в `session.init` (**строго аддитивно**, легаси-путь не тронут): `establishCoopIdSession` строит wharfkit Session поверх keystore; `ensureWalletUnlocked` — единая точка авто-unlock по PIN-кэшу перед подписью; авто-лок RAM 30 мин; `StorageAdapter` поверх IndexedDB. ### Проверки - `@coopenomics/auth`: vitest pin 7/7 + signing 16/16 + wallet/regression зелёные; `tsc --noEmit` EXIT 0; ESLint 0; `unbuild` OK. - desktop: ESLint 0 по всем изменённым файлам. Полный vue-tsc — в CI. ### Известный гейт (вынесен в задачу #35) Токены CoopID (`oidc/tokens.ts`) — **RAM-only**: CoopID-сессия не переживает F5 (ключ поднимется из PIN-кэша, токен потеряется). Нужна персистентность токенов (зеркало легаси) до подключения recovery как потребителя с reload-survival. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude added 3 commits 2026-06-15 08:14:31 +00:00
Первый кирпич моста подписи CoopID: тот же паттерн, что у signDocument/signTimestamp —
ключ берётся через пакет-внутреннюю readUnlockedKey(), наружу уходит только SIG_K1_.
Десктопный WalletPluginCoopId (следующий шаг) делегирует сюда wharfkit Session.sign,
чтобы транзакции подписывались без выдачи WIF (как Ledger/Anchor-плагины).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Супердит «без PIN» из Story 11.8. Двухуровневая защита: серверный vault шифруется
паролём (расшифровка один раз при входе), локальный кэш — ПИН тем же
Argon2id+AES-256-GCM (pin.ts: savePinProtected/loadPinProtected/hasPinProtected/
clearPinProtected, AAD pin|<account>). Обвязка в wallet: persistPinCache (после
входа), unlockWithPin (reload/авто-лок без пароля), hasPinCache/clearPinCache;
DEFAULT_PIN='000000' делает разблокировку прозрачной. Модель угроз: ПИН — анти-
«дурак», от кражи блоба защищает пароль. Тесты pin.test.ts 7/7, tsc 0, eslint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WalletPluginCoopId (AbstractWalletPlugin) считает signing-дайджест и делегирует
подпись в @coopenomics/auth.signChainDigest — приватный ключ из keystore не
выходит, как у Ledger/Anchor. session-store: establishCoopIdSession строит
wharfkit Session поверх keystore (без globalStore.wif); ensureUnlocked — единая
точка авто-unlock по PIN-кэшу перед каждой подписью; авто-лок RAM 30 мин
(скользящий); username/isAuth fallback на CoopID-аккаунт; close затирает ключ и
PIN-кэш. session.init: ветка CoopID после легаси — строго аддитивно, при
отсутствии CoopID-кэша no-op, легаси-путь байт-в-байт не изменён. StorageAdapter
поверх IndexedDB (createCoopIdStorage) + deleteFromIndexedDB. ESLint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-15 08:43:54 +00:00
@coopenomics/auth: configureTokenStorage + restoreSession — пара токенов сессии
персистится в StorageAdapter (frontend — IndexedDB) и восстанавливается на boot;
setSession пишет копию, refresh её обновляет, clearSession стирает (logout). Без
этого CoopID-сессия теряла токен на F5 и была слабее легаси. Тесты oidc-tokens
10/10 (персист/restore/refresh-update/clear), tsc 0, ESLint 0, dist пересобран.

Desktop: session.init восстанавливает токены CoopID (configureTokenStorage+
restoreSession) перед establishCoopIdSession — сессия переживает reload (токены из
IndexedDB + ключ из PIN-кэша). Recovery — первый потребитель моста: confirmRecovery
строит CoopID-сессию поверх keystore (у восстановленного пайщика легаси-WIF нет) +
PIN-кэш; RecoverConfirm ведёт по каноническому boot-пути на рабочий стол вместо
тупикового signin (который требует WIF). ESLint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ant added this to the CoopID project 2026-06-15 13:17:41 +00:00
Author
Owner

Схлопнут в единый PR #148 (coopID → dev). Ветка сохранена как резерв, ничего не потеряно.

Схлопнут в единый PR #148 (coopID → dev). Ветка сохранена как резерв, ничего не потеряно.
claude closed this pull request 2026-06-15 13:26:49 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#145