> ## Documentation Index
> Fetch the complete documentation index at: https://attesta.kyberon.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# MultiPartyChallenge

> Two or more independent approvers must each pass a different sub-challenge before critical actions execute

The `MultiPartyChallenge` is the strongest verification mechanism in Attesta. It requires **two or more independent approvers**, each completing a **different sub-challenge**, before a critical action is allowed to proceed. This prevents any single point of failure in the approval process.

## When It Is Used

By default, `MultiPartyChallenge` is assigned to **CRITICAL** risk actions (score `0.8`–`1.0`). These are irreversible, destructive operations like dropping databases, deleting production infrastructure, or purging user data.

| Parameter            | Default   | Description                                         |
| -------------------- | --------- | --------------------------------------------------- |
| `required_approvers` | **2**     | Number of independent approvers needed              |
| `min_review_seconds` | Inherited | Each sub-challenge uses its own minimum review time |

<Warning>
  The [Trust Engine](/concepts/trust-engine) enforces a safety invariant: **CRITICAL actions are never downgraded**, regardless of trust score. Even a maximally trusted agent will always face multi-party approval for critical operations.
</Warning>

***

## Sub-Challenge Rotation

Each approver receives a **different** sub-challenge, assigned in a rotating pattern:

| Approver | Sub-Challenge                                        | Min Review |
| -------- | ---------------------------------------------------- | ---------- |
| 1st      | [TeachBackChallenge](/concepts/challenge-teach-back) | 30.0s      |
| 2nd      | [QuizChallenge](/concepts/challenge-quiz)            | 10.0s      |
| 3rd      | [ConfirmChallenge](/concepts/challenge-confirm)      | 3.0s       |
| 4th      | TeachBackChallenge (rotation restarts)               | 30.0s      |
| 5th      | QuizChallenge                                        | 10.0s      |
| ...      | ...                                                  | ...        |

The rotation order is: **teach-back -> quiz -> confirm -> teach-back -> ...**

This design ensures:

1. **The first approver** must deeply understand the action (teach-back)
2. **The second approver** must demonstrate comprehension (quiz)
3. **Subsequent approvers** provide independent confirmation at lighter levels
4. **No two consecutive approvers** face the same challenge type

<Note>
  All approvers must pass their respective sub-challenges. If any single approver fails or denies, the entire multi-party challenge fails and the action is blocked.
</Note>

***

## Usage

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  from attesta.challenges import MultiPartyChallenge

  # Default: 2 approvers
  multi = MultiPartyChallenge()

  # Stricter: 3 approvers required
  multi = MultiPartyChallenge(required_approvers=3)

  # Use in a custom challenge map
  from attesta import Attesta, RiskLevel

  attesta = Attesta(
      challenge_map={
          RiskLevel.LOW: None,
          RiskLevel.MEDIUM: "confirm",
          RiskLevel.HIGH: "quiz",
          RiskLevel.CRITICAL: MultiPartyChallenge(required_approvers=3),
      }
  )
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import { MultiPartyChallenge, Attesta, RiskLevel } from "@kyberon/attesta";

  // Default: 2 approvers
  const multi = new MultiPartyChallenge();

  // Stricter: 3 approvers required
  const multi = new MultiPartyChallenge({ requiredApprovers: 3 });

  // Use in a custom challenge map
  const attesta = new Attesta({
    challengeMap: {
      [RiskLevel.LOW]: null,
      [RiskLevel.MEDIUM]: "confirm",
      [RiskLevel.HIGH]: "quiz",
      [RiskLevel.CRITICAL]: new MultiPartyChallenge({ requiredApprovers: 3 }),
    },
  });
  ```
</CodeGroup>

***

## Approval Flow

**When all approvers pass:**

| Stage             | Approver 1                                       | Approver 2          |
| ----------------- | ------------------------------------------------ | ------------------- |
| **Action**        | `drop_database("prod")` -- Risk: 0.92 (CRITICAL) | Same action context |
| **Sub-challenge** | Teach-Back (30.0s min)                           | Quiz (10.0s min)    |
| **Result**        | PASS                                             | PASS                |
| **Outcome**       | **ALL PASSED -- Action Executes**                |                     |

**When any approver fails:**

| Stage             | Approver 1                            | Approver 2 |
| ----------------- | ------------------------------------- | ---------- |
| **Sub-challenge** | Teach-Back                            | Quiz       |
| **Result**        | PASS                                  | FAIL       |
| **Outcome**       | **BLOCKED -- `AttestaDenied` raised** |            |

***

## Approver Identity

Each approver is identified by an `approver_id`, which is recorded in the [audit trail](/concepts/audit-trail). This enables post-hoc analysis of who approved what.

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  # Approver IDs are recorded in the audit entry
  {
      "action_name": "drop_database",
      "challenge_type": "multi_party",
      "challenge_passed": True,
      "approver_ids": ["alice@company.com", "bob@company.com"],
      "verdict": "approved",
  }
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  // Approver IDs are recorded in the audit entry
  {
    actionName: "drop_database",
    challengeType: "multi_party",
    challengePassed: true,
    approverIds: ["alice@company.com", "bob@company.com"],
    verdict: "approved",
  }
  ```
</CodeGroup>

<Tip>
  In production environments, integrate approver identification with your organization's SSO or identity provider. The approver ID should be a verifiable identity, not a self-reported name.
</Tip>

***

## Terminal Experience

The [TerminalRenderer](/concepts/renderers) presents multi-party challenges sequentially, showing progress:

**Approver 1 prompt (Teach-Back):**

> **CRITICAL RISK -- Multi-Party Approval Required** (Approvals: 0/2)
>
> **Action:** `drop_database` | **Arguments:** `database="production"` | **Risk:** 0.92 (CRITICAL)
>
> **APPROVER 1 -- Teach-Back**
> In your own words, explain what this action will do and what its impact will be. (minimum 15 words)

**After the first approver passes (Quiz):**

> **CRITICAL RISK -- Multi-Party Approval Required** (Approvals: 1/2)
>
> **APPROVER 2 -- Quiz**
> Q1: Which database will be dropped?
> a) staging, b) production, c) development, d) testing

***

## Configuration via YAML

```yaml attesta.yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
policy:
  challenge_map:
    critical: multi_party

  multi_party:
    required_approvers: 3

  min_review_seconds:
    teach_back: 30.0
    quiz: 10.0
    confirm: 3.0
```

### Scaling Approvers by Domain

[Domain profiles](/concepts/domain-profiles) can override the number of required approvers for specific action types:

```yaml attesta.yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
domains:
  - healthcare:
      escalation_rules:
        phi_access: 3        # PHI access requires 3 approvers
        data_export: 3       # Data export requires 3 approvers
  - infrastructure:
      escalation_rules:
        production_deploy: 2  # Production deploys need 2
        database_drop: 4      # Database drops need 4
```

<CardGroup cols={2}>
  <Card title="TeachBackChallenge" icon="chalkboard-user" href="/concepts/challenge-teach-back">
    The first sub-challenge in the rotation
  </Card>

  <Card title="Trust Engine" icon="brain" href="/concepts/trust-engine">
    Why CRITICAL actions are never downgraded
  </Card>
</CardGroup>
