PD-82 — Décomposition en tâches agents¶
Date : 2026-02-17 Branche : feature/PD-82-dual-validation Total tâches : 10
Vue d'ensemble¶
| Phase | Tâches | Description |
|---|---|---|
| Foundation | 1-3 | Entités, State Machine, Signature Verification |
| Core Logic | 4-6 | Service principal, Audit, Scheduler |
| API & Tests | 7-10 | Controller, DTOs, Tests |
Tâche 1 — Entities + Enums + Module setup¶
Agent : agent-developer Contract : CC-82-01 Dépendances : Aucune
Fichiers à produire :
src/modules/dual-validation/
├── dual-validation.module.ts
├── entities/
│ ├── dual-validation-request.entity.ts
│ └── validation-record.entity.ts
└── enums/
├── validation-state.enum.ts
└── validation-event-type.enum.ts
Invariants couverts : INV-82-06, INV-82-07, INV-82-09, INV-82-10 CA couverts : CA-82-04, CA-82-05
Contraintes : - Schema vault_secure - Timestamps precision 3 (ms) - Signature as bytea (Buffer) - eIDAS level: substantial | high only - tsr_blob: bytea pour preuve RFC 3161 (amélioration Gate 5)
Tâche 2 — State Machine Service¶
Agent : agent-developer Contract : CC-82-02 Dépendances : Tâche 1 (enums)
Fichiers à produire :
Invariants couverts : INV-82-01, INV-82-02, INV-82-03, INV-82-04, INV-82-08 CA couverts : CA-82-01, CA-82-02, CA-82-03, CA-82-06, CA-82-10
Contraintes : - ACTIVATED only with 2 valid, non-revoked validations - No implicit transitions (no auto-approve) - TTL = 168 hours UTC from firstValidationAt - Terminal states: ACTIVATED, REJECTED, EXPIRED (no outbound) - Order agnostic: parent/authority symmetric
State transitions :
PENDING_BOTH → PENDING_AUTHORITY (parent validates)
PENDING_BOTH → PENDING_PARENT (authority validates)
PENDING_AUTHORITY → ACTIVATED (authority validates)
PENDING_PARENT → ACTIVATED (parent validates)
PENDING_* → REJECTED (revocation)
PENDING_* → EXPIRED (TTL exceeded)
Tâche 3 — Signature Verification Service¶
Agent : agent-developer Contract : CC-82-04 Dépendances : Tâche 1 (entities)
Fichiers à produire :
src/modules/dual-validation/services/
├── signature-verification.service.ts
└── tsa-client.service.ts # Amélioration Gate 5
Invariants couverts : INV-82-09, INV-82-10 CA couverts : CA-82-04, CA-82-05
Contraintes : - Verify signature against certificate public key - Validate X.509v3 certificate chain (root CA trusted) - Check eIDAS level >= substantial - Reject expired/revoked certificates (OCSP with 1h cache, fallback CRL) - Log verification failures to audit - TSA client for RFC 3161 timestamp requests
Tâche 4 — Dual Validation Service¶
Agent : agent-developer Contract : CC-82-03 Dépendances : Tâches 1, 2, 3
Fichiers à produire :
src/modules/dual-validation/services/
└── dual-validation.service.ts
src/modules/dual-validation/interfaces/
├── dual-validation.interfaces.ts
└── validation-event.interfaces.ts
Invariants couverts : INV-82-01, INV-82-05, INV-82-11, INV-82-12 CA couverts : CA-82-01, CA-82-07, CA-82-08, CA-82-09
Contraintes : - activatePRE(delegationId) called only when state === ACTIVATED - activatePRE called exactly once per request (idempotency check) - All events logged via AuthAuditWriterService - Platform cannot bypass validation requirement - Authority resolved from internal registry (never from user input)
Methods :
createRequest(params): Promise<DualValidationRequest>
submitValidation(params): Promise<ValidationResult>
revokeValidation(params): Promise<RevocationResult>
getRequestStatus(requestId): Promise<RequestStatusDto>
Tâche 5 — Audit Log Integration¶
Agent : agent-developer Contract : CC-82-05 Dépendances : Tâche 4
Fichiers à modifier :
src/modules/auth-audit/interfaces/auth-audit.interfaces.ts # Add event types
src/modules/dual-validation/services/dual-validation.service.ts # Audit calls
Invariants couverts : INV-82-12 CA couverts : CA-82-08, CA-82-09
Contraintes : - Every state transition logged append-only - Invalid attempts logged with reason - Activation event references both validation IDs - Hash chain integrity (via PD-31)
Event types to add : - DVAL_REQUEST_CREATED - DVAL_PARENT_VALIDATED - DVAL_AUTHORITY_VALIDATED - DVAL_VALIDATION_REVOKED - DVAL_REQUEST_ACTIVATED - DVAL_REQUEST_EXPIRED - DVAL_INVALID_ATTEMPT
Tâche 6 — TTL Expiration Scheduler¶
Agent : agent-developer Contract : CC-82-07 Dépendances : Tâches 1, 4, 5
Fichiers à produire :
Invariants couverts : INV-82-02, INV-82-03 CA couverts : CA-82-03
Contraintes : - Cron job runs every 5 minutes - Transitions PENDING_* to EXPIRED if now > expiresAt - No implicit validation (explicit expiration only) - Logs expiration events via audit
Tâche 7 — Controller + DTOs + Guards¶
Agent : agent-developer Contract : CC-82-06 Dépendances : Tâche 4
Fichiers à produire :
src/modules/dual-validation/
├── controllers/
│ └── dual-validation.controller.ts
├── dto/
│ ├── create-validation-request.dto.ts
│ ├── submit-validation.dto.ts
│ ├── revoke-validation.dto.ts
│ └── validation-response.dto.ts
├── guards/
│ └── validator-identity.guard.ts
└── decorators/
└── validator-role.decorator.ts
Invariants couverts : INV-82-05, INV-82-07 CA couverts : CA-82-07
Endpoints : - POST /dual-validation/requests - create request - POST /dual-validation/requests/:id/validations - submit validation - DELETE /dual-validation/validations/:id - revoke own validation - GET /dual-validation/requests/:id - status
Contraintes : - Guard ensures validator identity matches JWT token - Authority ID never from user input - class-validator decorators for all DTOs
Tâche 8 — Unit Tests (State Machine + Service)¶
Agent : agent-qa-unit Contract : CC-82-08 Dépendances : Tâches 1-6
Fichiers à produire :
src/modules/dual-validation/__tests__/
├── state-machine.spec.ts
└── dual-validation.service.spec.ts
CA couverts : CA-82-01, CA-82-02, CA-82-03, CA-82-06, CA-82-10
Tests nominaux : - TC-NOM-01: Single validation does not activate - TC-NOM-02: Parent→Authority activation succeeds - TC-NOM-03: Authority→Parent activation succeeds - TC-NOM-04: Probatory trail complete after activation
Tests erreurs : - TC-ERR-01: Activation attempt with single validation → rejected - TC-ERR-02: Validation after TTL expiry → EXPIRED - TC-ERR-03: Revocation by non-author → rejected - TC-ERR-04: Revocation after ACTIVATED → rejected
Tests TTL boundary (amélioration Gate 5) : - TC-TTL-01: Validation at 167h59m → accepted - TC-TTL-02: Validation at 168h01m → rejected
Tâche 9 — Integration Tests (PD-41, PD-31)¶
Agent : agent-qa-unit Contract : CC-82-08 Dépendances : Tâches 1-7
Fichiers à produire :
CA couverts : CA-82-08, CA-82-09
Tests : - TC-INT-01: activatePRE called exactly once after 2 validations - TC-INT-02: Audit events written for each transition - TC-INT-03: Hash chain integrity maintained - TC-INT-04: Activation event references both validation IDs
Mocks : - PreService.activatePRE (PD-41) - AuthAuditWriterService.write (PD-31)
Tâche 10 — E2E Tests (API endpoints)¶
Agent : agent-qa-unit Contract : CC-82-08 Dépendances : Tâches 1-7
Fichiers à produire :
CA couverts : CA-82-07, CA-82-04, CA-82-05
Tests : - TC-E2E-01: Full flow parent→authority via HTTP - TC-E2E-02: Full flow authority→parent via HTTP - TC-E2E-03: Revocation via DELETE endpoint - TC-E2E-04: Unauthorized access rejected - TC-E2E-05: Invalid signature rejected
Migration SQL¶
À exécuter après Tâche 1 :
-- File: migrations/XXXXXX-create-dual-validation-tables.ts
CREATE SCHEMA IF NOT EXISTS vault_secure;
CREATE TABLE vault_secure.dual_validation_request (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
delegation_id UUID NOT NULL UNIQUE,
minor_vault_id UUID NOT NULL,
parent_user_id UUID NOT NULL,
authority_id UUID,
state VARCHAR(20) NOT NULL DEFAULT 'PENDING_BOTH',
created_at TIMESTAMPTZ(3) NOT NULL DEFAULT NOW(),
first_validation_at TIMESTAMPTZ(3),
activated_at TIMESTAMPTZ(3),
expires_at TIMESTAMPTZ(3),
CONSTRAINT chk_state CHECK (state IN (
'PENDING_BOTH', 'PENDING_AUTHORITY', 'PENDING_PARENT',
'ACTIVATED', 'REJECTED', 'EXPIRED'
))
);
CREATE TABLE vault_secure.validation_record (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
request_id UUID NOT NULL REFERENCES vault_secure.dual_validation_request(id),
validator_type VARCHAR(20) NOT NULL,
validator_id UUID NOT NULL,
validated_at TIMESTAMPTZ(3) NOT NULL,
signature BYTEA NOT NULL,
signature_algorithm VARCHAR(32) NOT NULL,
certificate_chain TEXT NOT NULL,
eidas_level VARCHAR(20) NOT NULL,
tsr_blob BYTEA, -- RFC 3161 timestamp response
revoked BOOLEAN NOT NULL DEFAULT FALSE,
revoked_at TIMESTAMPTZ(3),
event_hash CHAR(64) NOT NULL,
CONSTRAINT chk_validator_type CHECK (validator_type IN ('PARENT', 'AUTHORITY')),
CONSTRAINT chk_eidas_level CHECK (eidas_level IN ('substantial', 'high'))
);
CREATE INDEX idx_dval_request_state ON vault_secure.dual_validation_request(state);
CREATE INDEX idx_dval_request_expires ON vault_secure.dual_validation_request(expires_at)
WHERE state IN ('PENDING_AUTHORITY', 'PENDING_PARENT');
CREATE INDEX idx_validation_request ON vault_secure.validation_record(request_id);
-- Append-only trigger
CREATE OR REPLACE FUNCTION vault_secure.prevent_validation_mutation()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
RAISE EXCEPTION 'DELETE not allowed on validation_record (append-only)';
END IF;
IF TG_OP = 'UPDATE' AND OLD.id IS NOT NULL THEN
IF NEW.signature != OLD.signature OR NEW.certificate_chain != OLD.certificate_chain THEN
RAISE EXCEPTION 'UPDATE not allowed on immutable fields (append-only)';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_validation_append_only
BEFORE UPDATE OR DELETE ON vault_secure.validation_record
FOR EACH ROW EXECUTE FUNCTION vault_secure.prevent_validation_mutation();
Ordre d'exécution¶
Tâche 1 (Entities) ─────┬─── Tâche 2 (State Machine)
│
├─── Tâche 3 (Signature Verification)
│
└─── Tâche 4 (Service) ───┬─── Tâche 5 (Audit)
│
├─── Tâche 6 (Scheduler)
│
└─── Tâche 7 (Controller)
│
└─── Tâches 8, 9, 10 (Tests)
Décomposition effectuée le 2026-02-17 Workflow de gouvernance ProbatioVault