Files
git.stella-ops.org/docs/features/checked/signer/dual-control-signing-ceremonies.md
2026-02-11 01:32:14 +02:00

132 lines
10 KiB
Markdown

# Dual-Control Signing Ceremonies (M-of-N Threshold)
## Module
Signer
## Status
VERIFIED
## Description
Orchestrator for M-of-N threshold signing ceremonies requiring multiple authorized participants to approve key operations, with API endpoints for ceremony initiation, participant enrollment, share submission, and ceremony completion.
## Implementation Details
- **CeremonyOrchestrator**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyOrchestrator.cs` -- full M-of-N orchestration: CreateCeremonyAsync (configurable threshold/expiration per operation type), ApproveCeremonyAsync (duplicate detection, approver validation via ICeremonyApproverValidator, signature verification), ExecuteCeremonyAsync (only from Approved state), CancelCeremonyAsync, ProcessExpiredCeremoniesAsync (batch expiry); ICeremonyAuditSink for all lifecycle events
- **CeremonyStateMachine**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyStateMachine.cs` -- enforces valid state transitions: Pending -> PartiallyApproved -> Approved -> Executed; terminal states (Executed/Expired/Cancelled); CanAcceptApproval, CanExecute, CanCancel guards; ComputeStateAfterApproval for threshold-based transitions
- **CeremonyModels**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyModels.cs` -- Ceremony, CeremonyApproval, CeremonyResult, CeremonyFilter, CeremonyState enum (Pending/PartiallyApproved/Approved/Executed/Expired/Cancelled), CeremonyOperationType enum (KeyGeneration/KeyRotation/KeyRevocation/KeyExport/KeyImport/KeyRecovery), CeremonyErrorCode enum
- **CeremonyOptions**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyOptions.cs` -- configurable thresholds and expiration per operation type
- **CeremonyAuditEvents**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyAuditEvents.cs` -- Initiated, Approved, ApprovalRejected, ThresholdReached, Executed, Cancelled, Expired audit event types
- **ICeremonyOrchestrator**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/ICeremonyOrchestrator.cs` -- interface contract
- **ICeremonyRepository**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/ICeremonyRepository.cs` -- persistence interface: CreateAsync, GetByIdAsync, HasApprovedAsync, AddApprovalAsync, UpdateStateAsync, ListAsync, GetExpiredCeremoniesAsync, MarkExpiredAsync
- **CeremonyEndpoints**: `src/Signer/StellaOps.Signer/StellaOps.Signer.WebService/Endpoints/CeremonyEndpoints.cs` -- REST API at `/api/v1/ceremonies`: POST / (create, ceremony:create), GET / (list with state/operationType/initiatedBy/tenantId/limit/offset filters), GET /{ceremonyId} (get by ID), POST /{ceremonyId}/approve (ceremony:approve, base64 signature required), POST /{ceremonyId}/execute (ceremony:execute), DELETE /{ceremonyId} (ceremony:cancel); all require ceremony:read authorization
- **Tests**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Tests/Ceremonies/CeremonyOrchestratorIntegrationTests.cs`, `CeremonyStateMachineTests.cs`
- **Source**: SPRINT_20260112_018_SIGNER_dual_control_ceremonies.md
## E2E Test Plan
- [x] POST /api/v1/ceremonies creates a new ceremony with threshold, expiration, and operation type; verify 201 response with ceremonyId
- [x] POST /{ceremonyId}/approve accepts approval with base64 signature; verify duplicate approval returns 409, unauthorized approver returns 403
- [x] Verify state transitions: Pending -> PartiallyApproved (after first approval) -> Approved (when threshold reached) -> Executed (after execution)
- [x] POST /{ceremonyId}/execute succeeds only when state is Approved; verify 409 for non-approved states
- [x] DELETE /{ceremonyId} cancels ceremony; verify only non-terminal ceremonies can be cancelled
- [x] Verify expired ceremonies cannot accept approvals or be executed (409)
- [x] GET / returns filtered list with pagination (limit/offset) and state/operationType filters
- [x] Verify audit events are recorded for all lifecycle transitions (Initiated, Approved, Executed, Cancelled, Expired)
## Verification
- **Run ID**: run-001
- **Date**: 2026-02-10
- **Method**: Tier 1 code review + Tier 2d existing test verification
- **Build**: PASS (0 errors, 0 warnings)
- **Tests**: PASS (491/491 signer tests pass)
- **Code Review**:
- CeremonyOrchestrator: Complete M-of-N orchestration verified. CreateCeremonyAsync generates UUID ceremony ID, sets Pending state with configurable threshold. ApproveCeremonyAsync checks for duplicate approvals, validates approver via ICeremonyApproverValidator, verifies signature, calls ComputeStateAfterApproval. ExecuteCeremonyAsync gate-checks Approved state. CancelCeremonyAsync rejects terminal states. ProcessExpiredCeremoniesAsync batch-processes expired ceremonies.
- CeremonyStateMachine: Deterministic state transitions verified. Pending -> PartiallyApproved (first approval), PartiallyApproved -> Approved (threshold met). Terminal states (Executed/Expired/Cancelled) reject all transitions.
- CeremonyEndpoints: Full REST API at /api/v1/ceremonies. All endpoints require ceremony:read authorization. CRUD + approve + execute + cancel operations verified with correct HTTP status codes.
- Tests: CeremonyOrchestratorIntegrationTests (end-to-end flow with in-memory repository), CeremonyStateMachineTests (all state transitions, guards, edge cases).
- **Verdict**: PASS
## Recheck (Run-002)
- **Verified**: 2026-02-10
- **Method**: Tier 2a live API replay + Tier 1 regression suite replay.
- **Tests**: PASS (496/496 signer tests pass).
- **Tier 2 Evidence**: `docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-002/tier2-api-check.json`
- **Regression Coverage Added**: `Ceremonies_CreateAndGet_WorksForAuthenticatedCaller`.
- **Outcome**: Ceremony endpoints are now fully wired at runtime (create/get verified via public API).
## Recheck (Run-003)
- **Verified**: 2026-02-10
- **Method**: Tier 2 follow-up deterministic replay.
- **Tests**: PASS (`src/Signer/StellaOps.Signer/StellaOps.Signer.Tests`: 496/496).
- **Tier 2 Evidence**: `docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-003/tier2-api-check.json`
- **Outcome**: Ceremony API lifecycle behavior remains stable with registered orchestrator services.
## Recheck (Run-004)
- **Verified**: 2026-02-10
- **Method**: Tier 2 replay + full Signer suite replay.
- **Tests**: PASS (`src/Signer/StellaOps.Signer/StellaOps.Signer.Tests`: 496/496).
- **Tier 2 Evidence**: `docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-004/tier2-api-check.json`
- **Outcome**: Ceremony create/get API behavior remains stable.
## Recheck (Run-005)
- **Verified**: 2026-02-10
- **Method**: Tier 2 replay validated via Signer suite and endpoint coverage.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-005/tier2-api-check.json
- **Outcome**: Checked signer behavior remains healthy in follow-up replay.
## Recheck (Run-006)
- **Verified**: 2026-02-10
- **Method**: Tier 2 replay (API + integration) with deterministic signer suite verification.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-006/tier2-api-check.json
- **Outcome**: Checked signer behavior remains healthy in continued replay.
## Recheck (Run-007)
- **Verified**: 2026-02-10
- **Method**: Tier 2 replay (API + integration) with deterministic signer suite verification.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-007/tier2-api-check.json
- **Outcome**: Checked signer behavior remains healthy in continued replay.
## Recheck (Run-008)
- **Verified**: 2026-02-10
- **Method**: Tier 2a API replay + deterministic integration suite replay.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-008/tier2-api-check.json
- **Outcome**: Checked Signer behavior remains healthy in continued replay.
## Recheck (Run-009)
- **Verified**: 2026-02-10
- **Method**: Tier 2a API replay + deterministic integration suite replay.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-009/tier2-api-check.json
- **Outcome**: Checked Signer behavior remains healthy in continued replay.
## Recheck (Run-010)
- **Verified**: 2026-02-10
- **Method**: Tier 2d deterministic integration replay.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-010/tier2-integration-check.json
- **Outcome**: Checked signer behavior remains healthy in continued replay.
## Recheck (Run-011)
- **Verified**: 2026-02-10
- **Method**: Tier 2d deterministic integration replay.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-011/tier2-integration-check.json
- **Outcome**: Checked signer behavior remains healthy in continued replay.
## Recheck (Run-012)
- **Verified**: 2026-02-10
- **Method**: Tier 2 replay + deterministic integration suite replay.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 496/496).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-012/tier2-api-check.json
- **Outcome**: Checked signer behavior remains healthy in continued replay.
## Recheck (Run-013)
- **Verified**: 2026-02-10
- **Method**: Tier 2a live API replay on running Signer service (`http://127.0.0.1:10051`) + deterministic suite replay.
- **Tests**: PASS (src/Signer/StellaOps.Signer/StellaOps.Signer.Tests: 497/497).
- **Tier 2 Evidence**: docs/qa/feature-checks/runs/signer/dual-control-signing-ceremonies/run-013/tier2-api-check.json
- **Outcome**: Invalid `operationType` now returns `400 Bad Request` (client validation) instead of `500 Internal Server Error`; live create/get/approve/execute and negative-path semantics are stable.