AEGISdocs
Guides

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:

RoleResponsibilityRBAC Role
Risk LeadAssesses whether the risk of proceeding is acceptablerisk_lead
Security LeadAssesses whether security controls remain adequatesecurity_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  REJECTED

Key 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 total

Key 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

ConstraintValueConfigurable?
Required signatures2 (Risk Lead + Security Lead)No
Expiration window24 hoursYes (expiration_hours parameter)
Max expiration10 years (87,600 hours)No
Complexity gateCannot be overriddenNo
Signature algorithmBIP-322 (default) or HybridYes (provider parameter)

Audit Trail

Every override attempt generates a SignatureRecord with:

FieldDescription
signer_idCustodian identifier
rolerisk_lead or security_lead
signatureBase64-encoded signature
message_hashSHA-256 hex of canonical message
timestampUTC timestamp of signature
public_keyBase64-encoded public key
algorithmbip322-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.


On this page