Files
git.stella-ops.org/docs/features/checked/signer/dual-control-signing-ceremonies.md

5.3 KiB

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

  • POST /api/v1/ceremonies creates a new ceremony with threshold, expiration, and operation type; verify 201 response with ceremonyId
  • POST /{ceremonyId}/approve accepts approval with base64 signature; verify duplicate approval returns 409, unauthorized approver returns 403
  • Verify state transitions: Pending -> PartiallyApproved (after first approval) -> Approved (when threshold reached) -> Executed (after execution)
  • POST /{ceremonyId}/execute succeeds only when state is Approved; verify 409 for non-approved states
  • DELETE /{ceremonyId} cancels ceremony; verify only non-terminal ceremonies can be cancelled
  • Verify expired ceremonies cannot accept approvals or be executed (409)
  • GET / returns filtered list with pagination (limit/offset) and state/operationType filters
  • 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