Override Workflow
Two-key override approval for governance gate failures.
Two-key override approval for governance gate failures.
When a proposal fails one or more governance gates but must proceed (e.g., emergency hotfix), AEGIS requires two independent signatures from designated custodians before approving the override. This page explains the full workflow.
What Is an Override?
An override bypasses failed governance gates for a specific proposal. It is a controlled exception, not a workaround -- every override is signed, audited, and time-limited.
Constraints:
- The Complexity gate cannot be overridden (hard floor at 0.5). If complexity fails, the proposal must be redesigned.
- Override requests expire after 24 hours by default.
- Both signatures must be on the same message hash (canonical encoding of proposal ID, justification, and failed gates).
Who Signs?
Two distinct custodians, each holding a separate private key:
| Role | Responsibility | RBAC Role |
|---|---|---|
| Risk Lead | Assesses whether the risk of proceeding is acceptable | risk_lead |
| Security Lead | Assesses whether security controls remain adequate | security_lead |
Both signatures are required. A single person holding both keys does not satisfy the two-key requirement -- the roles must be assigned to different individuals.
Flow Diagram
Proposal fails gates
|
v
+------+-------+
| Override | state: PENDING
| Request | requested_by, justification, failed_gates
+------+--------+
|
Risk Lead reviews
|
+----v----+
| Sign | Risk Lead signs message_hash with private key
+---------+
|
v
+------+-------+
| Override | state: PARTIAL
| Request | risk_lead_signature populated
+------+--------+
|
Security Lead reviews
|
+----v----+
| Sign | Security Lead signs same message_hash
+---------+
|
v
+------+---------+
| DualSignature | Validates both signatures
| Validator | Checks expiration, roles, hashes
+------+---------+
|
+----+----+
| |
APPROVED REJECTEDKey Generation
Each custodian generates a keypair using the configured signature provider.
BIP-322 (Default)
from crypto.bip322_provider import BIP322Provider
import base64
provider = BIP322Provider()
private_key, public_key = provider.generate_keypair()
# private_key: 32 bytes (BIP-340 secret key)
# public_key: 32 bytes (x-only public key)
print(f"Private key: {base64.b64encode(private_key).decode()}")
print(f"Public key: {base64.b64encode(public_key).decode()}")Hybrid Post-Quantum (Ed25519 + ML-DSA-44)
from crypto import get_hybrid_provider
provider = get_hybrid_provider()
private_key, public_key = provider.generate_keypair()
# private_key: Ed25519 (32B) + ML-DSA-44 (2560B) = 2592B total
# public_key: Ed25519 (32B) + ML-DSA-44 (1312B) = 1344B totalKey Security
Private keys must be stored securely. Never commit private keys to version control or log them. Use environment variables or a secrets manager for distribution.
Code Example: Full Override Flow
Step 1: Create the Override Request
from aegis_governance import DualSignatureValidator
from workflows.override import OverrideRequest, OverrideState
from datetime import datetime, timezone
# Create validator (auto-selects best signature provider)
validator = DualSignatureValidator(expiration_hours=24)
# Create canonical message hash
msg_hash = validator.create_message_hash(
proposal_id="prop-2024-001",
justification="Emergency hotfix for production outage",
failed_gates=["risk"],
)
print(f"Message hash: {msg_hash}")
print(f"Algorithm: {validator.algorithm_name}")Step 2: Risk Lead Signs
import base64
# Risk Lead signs with their private key
risk_signature = provider.sign(
message_hash=bytes.fromhex(msg_hash),
private_key=risk_lead_private_key, # 32 bytes
)
risk_sig_b64 = base64.b64encode(risk_signature).decode()
risk_pub_b64 = base64.b64encode(risk_lead_public_key).decode()Step 3: Security Lead Signs
# Security Lead signs the SAME message hash
security_signature = provider.sign(
message_hash=bytes.fromhex(msg_hash),
private_key=security_lead_private_key,
)
sec_sig_b64 = base64.b64encode(security_signature).decode()
sec_pub_b64 = base64.b64encode(security_lead_public_key).decode()Step 4: Validate Both Signatures
# Validate each signature individually
risk_valid = validator.validate_signature(
signature=risk_sig_b64,
message_hash=msg_hash,
public_key=risk_pub_b64,
)
sec_valid = validator.validate_signature(
signature=sec_sig_b64,
message_hash=msg_hash,
public_key=sec_pub_b64,
)
print(f"Risk Lead signature valid: {risk_valid}")
print(f"Security Lead signature valid: {sec_valid}")Using OverrideWorkflow (Full Flow)
For production use, the OverrideWorkflow manages the complete lifecycle:
from aegis_governance import OverrideWorkflow
workflow = OverrideWorkflow(
proposal_id="prop-2024-001",
justification="Emergency hotfix for production outage",
failed_gates=["risk"],
requested_by="engineer-1",
)
# Risk Lead adds signature
ok, msg = workflow.add_signature(
signer_id="risk-lead-1",
role="risk_lead",
signature=risk_sig_b64,
public_key=risk_pub_b64,
)
# Security Lead adds signature
ok, msg = workflow.add_signature(
signer_id="sec-lead-1",
role="security_lead",
signature=sec_sig_b64,
public_key=sec_pub_b64,
)
# Get result
result = workflow.get_result()
if result.approved:
print(f"Override approved (expires: {result.expires_at})")
else:
print(f"Override state: {result.state}")Using the Approver Actor
The Approver actor wraps override authorization with RBAC enforcement:
from aegis_governance import Approver
from crypto.bip322_provider import BIP322Provider
# Create approver with signature verification
approver = Approver(
actor_id="risk-lead-1",
name="Risk Lead",
is_risk_lead=True,
signature_verifier=BIP322Provider(),
)
result = approver.authorize_override(
proposal_id="prop-2024-001",
failed_gates=["risk"],
justification="Emergency hotfix",
signature=risk_sig_b64,
public_key=risk_lead_public_key, # 32 bytes
)
if result.success:
print("Override authorized by Risk Lead")Override Constraints
| Constraint | Value | Configurable? |
|---|---|---|
| Required signatures | 2 (Risk Lead + Security Lead) | No |
| Expiration window | 24 hours | Yes (expiration_hours parameter) |
| Max expiration | 10 years (87,600 hours) | No |
| Complexity gate | Cannot be overridden | No |
| Signature algorithm | BIP-322 (default) or Hybrid | Yes (provider parameter) |
Audit Trail
Every override attempt generates a SignatureRecord with:
| Field | Description |
|---|---|
signer_id | Custodian identifier |
role | risk_lead or security_lead |
signature | Base64-encoded signature |
message_hash | SHA-256 hex of canonical message |
timestamp | UTC timestamp of signature |
public_key | Base64-encoded public key |
algorithm | bip322-simple, ed25519, or hybrid-ed25519-mldsa44 |
Both successful and failed override attempts are logged in the telemetry pipeline with full signature records for forensic analysis.
Post-Quantum Option
For defense against quantum computing threats, use the hybrid provider:
from crypto import get_hybrid_provider
from aegis_governance import DualSignatureValidator
hybrid = get_hybrid_provider()
validator = DualSignatureValidator(provider=hybrid)
# Signatures now use Ed25519 + ML-DSA-44 (both must verify)Hybrid signatures are larger (~2,560 bytes vs 64 bytes for BIP-322) but provide post-quantum resistance.
Related Documentation
- Crypto Overview -- Entry point for all crypto in AEGIS
- Key Management Guide -- KEK lifecycle and rotation
- Python SDK: Key Management -- API reference