Skip to main content
After the risk scorer produces a score and the risk level is determined, Attesta selects a challenge — a verification step the human operator must complete before the action proceeds. Challenges scale in difficulty with risk: low-risk actions pass through automatically, while critical actions require multiple independent approvers.

Default Challenge Map

Risk LevelScore RangeChallengeMin Review Time
LOW0.0–0.3Auto-approve (no challenge)
MEDIUM0.3–0.6ConfirmChallenge3.0s
HIGH0.6–0.8QuizChallenge10.0s
CRITICAL0.8–1.0MultiPartyChallenge30.0s
The minimum review time prevents “rubber-stamping” — approvals that happen too fast to indicate genuine review. If an operator responds before the minimum time elapses, the challenge is flagged in the audit trail.

The Four Challenge Types

Confirm

Simple Y/N prompt with a mandatory pause. Suitable for state-changing but well-understood actions.

Quiz

Auto-generated comprehension questions from the action context. Forces the operator to read before approving.

Teach-Back

Free-text explanation of what the action will do. Validates understanding through keyword matching and pluggable validators.

Multi-Party

Requires 2+ independent approvers, each completing a different sub-challenge. The strongest verification for irreversible operations.

How Challenge Selection Works

The challenge pipeline follows this sequence:
1. Gated function called
2. Risk scorer produces score (0.0–1.0)
3. Trust engine adjusts effective risk (optional)
4. RiskLevel.from_score() classifies the level
5. Challenge map selects the challenge type
6. Challenge is presented to the operator
7. Result recorded in audit trail
from attesta import Attesta, RiskLevel
from attesta.challenges import (
    ConfirmChallenge,
    QuizChallenge,
    TeachBackChallenge,
    MultiPartyChallenge,
)

# Default challenge map (built-in)
attesta = Attesta()

# Custom challenge map
attesta = Attesta(
    challenge_map={
        RiskLevel.LOW: None,                     # auto-approve
        RiskLevel.MEDIUM: ConfirmChallenge(),
        RiskLevel.HIGH: QuizChallenge(max_questions=2),
        RiskLevel.CRITICAL: MultiPartyChallenge(required_approvers=3),
    }
)

Challenge Flow Diagram

1

Gated function called

The decorated function is intercepted before execution.
2

Risk scoring (5 factors)

The risk scorer evaluates function name, arguments, hints, domain patterns, and amplifiers to produce a score from 0.0 to 1.0.
3

Trust adjustment (if enabled)

The trust engine may lower the effective risk for agents with a proven track record.
4

Challenge selection by risk level

Risk LevelScore RangeChallenge
LOWbelow 0.3Auto-approve
MEDIUM0.3 — 0.6Confirm (Y/N)
HIGH0.6 — 0.8Quiz (1—3 questions)
CRITICAL0.8 — 1.0Multi-party (2+ approvers)
5

Audit trail (hash chain)

The result — approved, denied, or timed out — is recorded in a tamper-proof, hash-chained audit log.

Minimum Review Times

Every challenge type enforces a minimum review time. If the operator responds faster than this threshold, the approval is still accepted, but it is flagged as a potential “rubber stamp” in the audit trail.
ChallengeDefault Min ReviewRationale
Confirm3.0 secondsEnough time to read the action summary
Quiz10.0 secondsEnough time to read and answer questions
Teach-Back30.0 secondsEnough time to compose a meaningful explanation
Multi-PartyInherited from sub-challengesEach approver has their own minimum

Customizing Review Times

from attesta.challenges import QuizChallenge

# Require at least 15 seconds of review for quiz challenges
quiz = QuizChallenge(
    max_questions=3,
    min_correct=2,
    min_review_seconds=15.0,
)

Challenge Outcomes

Every challenge produces one of three outcomes:
OutcomeEffectAudit Field
PassedAction is executedchallenge_passed: true
FailedAttestaDenied raised, action blockedchallenge_passed: false
Timed outTreated as failure, action blockedchallenge_passed: false
When a challenge fails or is denied, the protected function is never executed. Attesta raises an AttestaDenied exception that the calling code must handle.
from attesta import AttestaDenied

try:
    result = delete_user("usr_12345")
except AttestaDenied as e:
    print(f"Action blocked: {e.reason}")
    print(f"Risk score: {e.risk_score}")
    print(f"Challenge type: {e.challenge_type}")

Configuration via YAML

The challenge map and review times can be configured declaratively:
attesta.yaml
policy:
  challenge_map:
    low: auto
    medium: confirm
    high: quiz
    critical: multi_party

  min_review_seconds:
    confirm: 5.0
    quiz: 15.0
    teach_back: 30.0

  multi_party:
    required_approvers: 2

ConfirmChallenge

Simple approval with mandatory pause

QuizChallenge

Auto-generated comprehension questions