CoopID — единый PR компонента 42 «Удостоверение пайщика» (схлопнутый стек #123–#146) #148
Open
claude
wants to merge 94 commits from
coopID into dev
pull from: coopID
merge into: C9S:dev
C9S:main
C9S:dev
C9S:marketplace2
C9S:testnet
C9S:fix/598-51-membership-fee-and-type-cleanup
C9S:feat/capital-canon
C9S:feat/598-supplier-registry
C9S:feature/capital-cpp-private-data-templates
C9S:fix/soviet-install-validation
C9S:feat/ku-selforg
C9S:feat/membership-exit
C9S:feat/marketplace-admin-orders
C9S:fix/capital-share-onproject-event
C9S:ci/independent-env-deploy
C9S:fix/registration-pending-display
C9S:feat/expense-chassis-mvp
C9S:fix/zip-utf8-flag
C9S:feat/coopid-auth-ui
C9S:feat/coopid-7-signing-bridge
C9S:feat/coopid-12-recovery-account-from-confirm
C9S:feat/coopid-epic-9-metrics
C9S:feat/coopid-epic-6-access-rules-cleanup
C9S:feat/coopid-epic-12
C9S:feat/coopid-epic-11
C9S:feat/coopid-epic-7
C9S:feat/coopid-epic-9
C9S:feat/coopid-epic-6
C9S:feat/marketplace-process-registry
C9S:feat/marketplace-notifications-map
C9S:feat/marketplace-ku-economy
C9S:savanna
C9S:feat/apps-catalog-finish
C9S:feat/epic-13-billing-package-powerup
C9S:feat/coopid-epic-4
C9S:feat/coopid-epic-3
C9S:feat/blagorost-on-chassis
C9S:feat/coopid-epic-1
C9S:feat/marketplace-coop-stock
C9S:feat/marketplace-live-catalog
C9S:feat/coop-invest-blagorost
C9S:feat/ff-release-flow
C9S:fix/marketplace-offer-instance-not-template
C9S:fix/extension-config-lost-update
C9S:feature/C28-28-email-relay
C9S:feat/E16-onsite-sign-gate
C9S:feat/pwa-version-watch
C9S:fix/participants-registry-status-badges
C9S:feat/E16-coop-categories
C9S:fix/sanitize-blockchain-error-toast
C9S:fix/wallet-withdraw-no-phantom-payment
C9S:fix/agenda-program-offer-links
C9S:feat/notification-center-dc-v3
C9S:fix/chatcoop-livekit-room-name
C9S:feat/delete-inactive-participant
C9S:feat/registration-ledger2-suspense
C9S:feat/registration-refund
C9S:feat/registration-payment-dedup
C9S:perf/factory-parallel-fetch
C9S:feat/E16-sklad-rework
C9S:fix/decision-createproject-tsc
C9S:perf/factory-weasyprint-warm-pool
C9S:feat/E16-korzina-zakaz-agregat
C9S:fix/agenda-pending-new-question
C9S:feat/soviet-negative-consensus
C9S:feat/registration-payment-decline
C9S:feat/coop-standards-audit
C9S:parser2
C9S:feat/mkt-return-chairman-cosign
C9S:feat/registration-fsm
C9S:feat/pwa-reliable-update
C9S:parser2-block-time-and-version
C9S:fix/gorozhane-phantom-cleanup
C9S:fix/remove-bank-kpp
C9S:feat/E9-3b-sub-subscribe
C9S:feat/E9-9-approve-moderation
C9S:feat/E10-5-on-chain-watcher
C9S:feat/E10-4-install-pipeline
C9S:feat/E9-3b-ui-publish-package
C9S:feat/E1-tspp-docs-real-text
C9S:feat/E10-3b-dynamic-supergraph
C9S:apps-catalog
C9S:feat/E9-9-3-b-publish-mutation
C9S:feat/sig-v2-export
C9S:feat/E10-1-federation-subgraph
C9S:parser2-epic-4-forks
C9S:feat/decision-authorize-backend
C9S:feat/blagorost-program-expenses-ui
C9S:parser2-epic-6-canonical-storage
C9S:wip/pre-e16-desktop-docs
C9S:feat/E15-min-volume-per-ku
C9S:feat/blagorost-E2-E5-program-expenses
C9S:feat/epic-14-billing-notifications
C9S:blagorost
C9S:feat/E14-explicit-shipment
C9S:feat/marketplace-collective-supply
C9S:feat/blagorost-E1-contract
C9S:feat/C28-21-pg-signed-documents
C9S:provider-billing
C9S:fix/desktop-format-asset-errors
C9S:feat/marketplace-docs
C9S:feat/epic-12-billing-subscriptions
C9S:fix/min-soviet-members-3
C9S:parser2-epic-3-transport
C9S:feat/marketplace2-drop-blocked
C9S:worktree-design-wave1
C9S:feat/notif-mobile-canon
C9S:fix/vue-tsc-desktop
C9S:worktree-marketplace-docs-impl
C9S:feat/chatcoop-hide-matrix-room-id
C9S:feat/epic-8-coop-registry-provider
C9S:parser2-epic-2-idempotency
C9S:feat/chatcoop-secretary-rooms
C9S:feat/remove-l3-blocked
C9S:ci-trial-typecheck-2026-05-23
C9S:fix/controller-revert-transpileonly
C9S:chore/mongo-standalone-local
C9S:chore/ci-unify-release-workflows
C9S:feat/controller-swc-builder
C9S:feat/chatcoop-transcription-memo
C9S:fix/mono-base-ca-certificates
C9S:fix/wallet-withdraw-gateway-timing
C9S:feat/ledger2-burn-blocked-withdraw
C9S:fix/boot-tests-wallet-signagree-v2
C9S:chore/trustees-ownership
C9S:feat/capital-w-cap-gen-cooperative
C9S:chore/re-review-E5-apl-fail-fast
C9S:chore/re-review-E4-fail-fast-tx-hash
C9S:chore/re-review-E3-fix-withdraw-stub
C9S:chore/review-E3-vitrina-fixes
C9S:chore/review-E10-design-fixes
C9S:chore/review-E8-spisanie-fixes
C9S:chore/review-E7-vozvrat-fixes
C9S:chore/review-E6-vydacha-fixes
C9S:chore/review-E5-postavka-fixes
C9S:chore/review-E4-order-fixes
C9S:feat/epic0-onboarding-harness
C9S:chore/marketplace2-sync-dev-and-controller-di
C9S:feat/E11-stol-zakazov-frontend-and-tests
C9S:feat/E11-stol-zakazov-final
C9S:feat/capital-artifacts-access
C9S:fix/floor-asset-display
C9S:feat/epic-9-warehouse-reporting
C9S:feat/598-11-epic-8-writeoff
C9S:feat/E7-garantiynyy-vozvrat
C9S:design
C9S:fix/capital-revert-394-generation-convert-refs
C9S:fix/is-unioned-zod-string-parse
C9S:fix/capital-rename-generation-convert-to-money-invest
C9S:feat/E6-vydacha-payshchiku
C9S:worktree-remove-1081-1082-rename-1080
C9S:feat/init-by-server-from-provider
C9S:feat/E5-postavka-priemka
C9S:feat/E11-techdebt-L12-split-payout
C9S:feat/E4-zakaz
C9S:gh-pages
C9S:feat/E3-vitrina-canon
C9S:feat/E11-S1-ledger2-marketplace-ops
C9S:file-storage
C9S:feat/1-10-marketplace-registration-offer-status
C9S:feat/1-9-marketplace-board-acceptance
C9S:feat/1-8-marketplace-access-matrix
C9S:feat/1-7-marketplace-template-registry
C9S:feat/1-6-marketplace-role-guard
C9S:feat/1-5-marketplace-member-wallet
C9S:feat/1-4-marketplace-onboarding-gate
C9S:feat/1-3-marketplace-access-by-membership
C9S:feat/1-1-install-from-catalog
C9S:ci/lock-tags-only
C9S:onboarding
C9S:reports
C9S:ledger3
C9S:graph-standarts
C9S:feat/ledger-double-entry
C9S:feat/docs-enrichment
C9S:feat/documentation
C9S:feat/marketplace-orders
C9S:feat/interops-extension-system
C9S:cursor/development-environment-setup-2882
C9S:feat/unified-test-pipeline
C9S:fix/security-vulnerabilities
C9S:audio-stream
C9S:project-authorization-on-create
C9S:refactoring
C9S:capital
C9S:element
C9S:ledger
C9S:marketplace
C9S:notifications2
C9S:notifications
C9S:document2
C9S:TSK-900
C9S:TSK-878
C9S:dacom-dark-sun/sprint-4
C9S:uniconstructor/sprint-5
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "coopID"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
CoopID — единый PR компонента 42 «Удостоверение пайщика»
Консолидация всего стека CoopID (Эпики 1–12) в один PR в
dev. Заменяет стек из 13 stacked-PR (#123, #127, #133, #134, #138, #139, #140, #141, #142, #143, #144, #145, #146) — он схлопнут без потерь: каждая ветка стека проверенно является предком верхушкиfeat/coopid-auth-ui, веткаcoopIDсоздана из её коммита.Объём: 91 коммит, 408 файлов, +28373 / −181.
Затронуто:
components/controller(auth-v2, резолверы, миграции),components/auth(CoopID REST/crypto),components/sdk(Zeus + login-фасад),components/desktop(вход/миграция/сессии/PIN),infra/coopid(authentik blueprint, Caddy),components/migrator,components/notifications.Что входит (по эпикам)
access_rules(#142)./metrics+ auth-метрики (#143)./coop/refresh(#139); мост подписиsignChainDigest+WalletPluginCoopId+ PIN-кэш + CoopID-сессия (#145).Готовность к тестам
Работает уже сейчас (без authentik): вход по ключу (легаси, без регрессий); страница «Настройки» на столе пайщика; список активных сессий + завершение сессий (GraphQL
AccountSecurityResolver); UI новых флоу (предложение задать пароль при вставке ключа, карточка PIN). Предусловия любого теста: контроллер крутит текущий код, применены миграции auth-v2 через migration-cli.Заблокировано до инфры: реальный вход по паролю и
migrate()/set_passwordтребуют развёрнутого authentik (Эпик 5: публичный SPA OIDC-клиент + envCOOPID_ISSUER/COOPID_CLIENT_ID) и SDK-фасада резолва account при входе по паролю (отдельная задача). Без них новые пути деградируют с чистой ошибкой, вход по ключу всегда работает.Старые ветки стека НЕ удалены — сохранены как резерв; PR #123–#146 закрыты в пользу этого.
🤖 Generated with Claude Code
Story 9.8. Скрипт infra/coopid/scripts/coopid-backup.sh делает pg_basebackup всего PG-кластера (authentik_db + coop_domain_db одним gzip-tar) и заливает в s3://{bucket}/{coopname}/{YYYY-MM-DD}.tar.gz с SSE-S3, чтобы восстановить кооператив за любой день. Retention 30 дней — через S3 lifecycle rule (режим --setup), не ручным удалением в cron. Каждый прогон пишет audit_events coopid.backup.created (s3_key, size_bytes, duration_seconds) прямым INSERT — бэкап отрабатывает даже при лежащем coopback. Режимы --setup и --dry-run. README: раздел Бэкапы с env, crontab 03:00 UTC и требованиями к PG. Cron ставит прод-плейбук. shellcheck зелёный; S3-путь и lifecycle 30d проверены против MinIO. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Story 6.3 Эпика 6 (Подход 4 «Управление и авторизация», CoopID компонент 42). PolicyHandler-контракт (IPolicyHandler {name; evaluate(ctx)}), декораторы @PolicyHandler(name) и @CheckAbility(action,subject,{policy?}), PolicyRegistry на DiscoveryService (fail-closed на неизвестное имя, throw на дубль), политика-образец SameCoopVotingPolicy с DB-lookup членства по participant-vault. Wiring через DiscoveryModule в AuthorizationModule. Фикс субстрата Layer 2: forward-миграция V2.4.8 реконсилирует access_rules — init V2.4.0 создавал плейсхолдер со старой схемой, V2.4.7 (6.2) был no-op → repo упал бы на column subject_type does not exist. DROP пустого плейсхолдера + CREATE правильной схемы, идемпотентно, канон append-only. Тесты: 4 registry + 5 policy unit + 1 integration с реальной coop_domain_db; coopback рестартнул чисто, PolicyRegistry обнаружил same-coop-voting через DiscoveryService. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>RecoveryFinalizationPlaceholder (кидал 503) заменён на RecoveryFinalizationService. Ротация active-ключа — существующим путём registrator::changekey (registrator владеет аккаунтами пайщиков; подпись ключом кооператива из vault), без нового authentik-пути. Порядок: новый vault-блоб → changekey → revokeAll сессий → audit KeyRotated {trigger,old_pubkey,new_pubkey,initiator_id} (Story 8.4) → уведомление пайщику SecurityEventKind.KeyRotated. vault сохраняется ДО on-chain переключения (новый приватный ключ живёт только в блобе — иначе сбой залочил бы пайщика). Пароль authentik в recovery НЕ трогается — это Эпик 5 (контроллер пока только читает authentik). Мультисиг для self-recovery не нужен (подтверждён факторами пайщика); защита от единоличного захвата — только force-recovery (6.9). Тесты: recovery-finalization 4 + регресс recovery-confirm 9 = 13/13. ESLint 0; coopback: Nest application successfully started, без DI-ошибок. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Все OIDC-операции выполняет authentik — контроллер узнаёт о них только через native-события, доставляемые webhook'ом на /coop/internal/authentik-events (механизм Story 1.5, shared-токен constant-time). Расширяем его на OIDC. - infra/coopid/authentik/blueprints/coopid-oidc-audit.yaml — event-matcher + notification-rule + policy-binding на каждое действие (login/logout/ authorize_application/login_failed/suspicious_request) + webhook body-mapping (action/user/client_ip/app/created) + transport на тот же endpoint. Поля сверены со /blueprints/schema.json образа 2026.2. - authentik-events.controller.ts — mapAuthentikEvent расширен: login→OidcLoginSuccess, logout→OidcLogout, authorize_application→OidcTokenIssued (семантические Oidc*); прочие native-события → Authentik<Action> (login_failed→AuthentikLoginFailed, suspicious_request→AuthentikSuspiciousRequest, failure-result). ip=client_ip, context={authentik_action,app,authentik_created}. policy_execution (Story 1.5) без изменений. Контекст проходит secret-blacklist аудита. - authentik-events.controller.spec.ts — 10 кейсов (weak-password, Oidc*, Authentik*, null, secret-blacklist). Доставка идёт по членам destination_group: пустая группа НЕ доставляет (транспорт не вызывается), поэтому выделенная группа coopid-audit с неактивным сервис-членом coopid-audit-sink — надёжная доставка webhook'ом без спама реальных админов. Live-валидация (стек поднят, потом потушен): blueprint применился (successful, объекты+членство в БД); прямой webhook login→OidcLoginSuccess и login_failed→ AuthentikLoginFailed записались в audit_events; РЕАЛЬНЫЙ login_failed в authentik прошёл всю цепочку (event→rule→webhook по docker-сети→контроллер→audit_events: AuthentikLoginFailed, ip, user=akadmin). Unit 10/10, ESLint 0. Отклонение (честно): OidcTokenRevoked/refresh не подключены — в authentik 2026.2 нет надёжного native-action для отзыва/refresh OAuth-токена (enum eventmatcher не содержит); endpoint'ы revoke/introspect работают, их аудит — отдельный механизм при подключении потребителей. Отложено. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Story 11.6 (логическая часть, без вёрстки). Не-визуальный, аддитивный seam, который вёрстка LoginForm/мастера потом просто наденет: - looksLikeWif(value) в shared/lib/utils — авторитетный детект ключа через WharfKit-парсер (5…/PVT_K1_…, отсекает пароли). Триггер «вставили ключ → предложить миграцию», а не вход ключом как раньше. - useLoginUser().migrateAndLogin({email, privateKey, newPassword}): SDK migrate() (Story 11.4 — подпись против COOPOS + set_password authentik + шифр ключа паролём в server-vault) → затем легаси-вход тем же ключом. Пайщик переходит на пароль и СРАЗУ остаётся в системе, без потери доступа и без зависимости от готовности OIDC-инфраструктуры authentik. Легаси login(email,wif) не тронут. Вёрстка (LoginForm email+пароль, мастер, баннер) и вход-по-паролю (нужен публичный OIDC-клиент authentik + резолв account из сессии) — отдельным заходом с визуальной проверкой/инфрой. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Был notImplemented-stub. Теперь полный confirm-флоу + повторный вход: генерация новой пары (старый ключ при восстановлении утрачен), шифрование приватного новым паролём в vault (AAD=субъект, наружу не уходит), POST /coop/recovery/confirm {token, TOTP, public_key, vault, password} → сервер (12.1) ставит пароль в authentik, сохраняет vault, ротирует active-ключ и отзывает сессии; затем authentik новым паролём → unlockWallet (round-trip нового блоба) → timestamp-handshake. Зачем: без этого фронт-recovery (12.3) нечем подтверждать — пайщик не мог завершить восстановление и войти под новым ключом/паролём. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>@willsoto/nestjs-prometheus + prom-client → GET /metrics в exposition-формате и процессные метрики Node на глобальном реестре. AuthMetricsService даёт доменные счётчики auth_login_attempts_total / auth_login_success_total / auth_errors_total{contour,error_code} (success_rate = производное PromQL, связка для alert Story 7.12). Провязка в единой login-границе verify-timestamp: попытка/успех/ошибка по типизированному коду, side-effect-only, вход не валит. Cross-cutting части AC (HTTP-latency-интерсептор, Redis/PG-gauge) отложены — не вшиваю в общий app вслепую. Тесты auth-metrics 6 + verify-timestamp 28. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.