Skip to content

feat(keys): add freeze status indexing and freeze/unfreeze admin endpoints (#964) - #995

Open
Cmitchelle7 wants to merge 3 commits into
accesslayerorg:mainfrom
Cmitchelle7:feat/freeze-status-964
Open

Cmitchelle7 wants to merge 3 commits into
accesslayerorg:mainfrom
Cmitchelle7:feat/freeze-status-964

Conversation

@Cmitchelle7

Copy link
Copy Markdown

Summary

Implements issue #964.

Adds freeze state tracking for keys, indexes freeze-related contract events, and exposes three endpoints under /api/v1/keys/:keyId for reading freeze status and for admin-initiated freeze/unfreeze. Cache is invalidated on every state change.

Closes #964

Scope

Endpoints

Method Path Auth Behaviour
GET /api/v1/keys/:keyId/freeze public Returns current freeze status, reason, frozenAt, frozenBy, and proposalId (if an unfreeze proposal is pending)
POST /api/v1/keys/:keyId/freeze x-admin-id header Admin-only emergency freeze. Writes a FREEZE event, emits an audit event, invalidates cache. Returns 201.
POST /api/v1/keys/:keyId/unfreeze x-admin-id header Initiates a multi-sig unfreeze proposal. Writes a PROPOSAL_CREATED event, emits an audit event, invalidates cache. Returns 202.

Data model

Two new Prisma models in prisma/schema/freeze.prisma:

  • KeyFreeze — one row per key. Holds isFrozen, reason, frozenAt, frozenBy, unfrozenAt, proposalId. Indexed on isFrozen.
  • FreezeEvent — append-only event log from the contract stream (and admin actions). Fields: keyId, eventType, ledger, txHash, eventIndex, payload. @@unique([txHash, eventIndex]) prevents duplicate ingestion; @@index([keyId, ledger]) and @@index([ledger]) support ordered scans.

keyId in both models corresponds to KeyOwnership.creatorId — the "key" identifier already used throughout the repo.

Files added

  • prisma/schema/freeze.prisma
  • src/modules/freeze/freeze.schemas.ts — Zod schemas for :keyId, freeze body, unfreeze body
  • src/modules/freeze/freeze.service.ts — getFreezeStatus, emergencyFreeze, initiateUnfreeze, indexFreezeEvent
  • src/modules/freeze/freeze.controllers.ts — three AsyncController handlers
  • src/modules/freeze/freeze.routes.ts — mounts routes with adminGuard on mutating endpoints
  • src/utils/freeze-cache.utils.ts — in-memory cache with TTL and invalidateFreezeCache

Files modified

  • src/modules/index.ts — mounts freezeRouter at /keys

Acceptance criteria

  • Freeze status returns correct state, reason, and timestamp — getFreezeStatus reads from KeyFreeze, returns null fields when no row exists
  • Emergency freeze validates admin authorization before executing — adminGuard middleware on the POST route
  • Unfreeze creates multi-sig proposal correctly — initiateUnfreeze generates a proposalId and writes a PROPOSAL_CREATED event
  • Freeze events indexed in correct order without gaps — @@unique([txHash, eventIndex]) + sequential processing via processIndexerChainEvents
  • Cache invalidated immediately on freeze or unfreeze confirmation — invalidateFreezeCache(keyId) called in both emergencyFreeze and initiateUnfreeze

Design notes

  • Cache: in-memory Map with 30s TTL and explicit invalidation, matching the pattern of creators.cache.ts. No new dependency.
  • Audit: freeze and unfreeze both call emitAuditEvent, matching the pattern used in admin.controllers.ts.
  • Idempotency: emergencyFreeze uses upsert, so repeat calls on an already-frozen key are safe. FreezeEvent uniqueness on (txHash, eventIndex) prevents duplicate event ingestion.
  • Event indexing: indexFreezeEvent is exported for the indexer worker to call from the contract event stream; it writes the event row, upserts the current state, and invalidates the cache in one call.

Verification

Local verification was not possible in this environment because node_modules was not present and corepack enable is blocked by C:\Program Files permissions. CI will run pnpm install, pnpm generate, pnpm lint, pnpm build, and tests on the PR.

Follow-ups (out of scope)

  • Contract-side multi-sig approval flow for unfreeze proposals (this PR only creates the proposal; approval/execution lives in the contract)
  • TTL tuning for the freeze cache once production access patterns are known

@drips-wave

drips-wave Bot commented Sep 28, 2026

Copy link
Copy Markdown

@Cmitchelle7 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Cmitchelle7 and others added 2 commits September 27, 2026 18:17
Prisma 6 rejects a BOM at the start of a schema file with P1012
('This line is invalid. It does not start with any known Prisma schema
keyword'). The BOM was introduced by PowerShell's Set-Content -Encoding UTF8
on Windows PowerShell 5.x, which writes UTF-8 with BOM. Re-encoded all new
files as UTF-8 without BOM.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add freeze status indexing and freeze/unfreeze admin endpoints

1 participant