Skip to content

panic

panic

Emergency panic — immediately cut all resource access across all projects.

Three-step sequence: quarantine → kill supervisors → (optionally) kill the containers themselves. Quarantine alone is not enough: each container's supervisor talks to its container over a bind-mounted unix socket, and nft sees no traffic there. Killing the host-side supervisor process is what actually denies the container any more vault / clearance / signer cycles.

Phase 1 (always): in parallel,

  • Quarantine every running container's shield (nft blackhole).
  • Kill every supervisor via the per-container PID files under state_root()/pids/ — SIGKILL, no graceful TERM, because panic targets misbehaving containers and the SIGTERM grace period would just hand the supervisor more time to answer socket calls. The gate runs inside each container's supervisor, so this same kill stops the gate too — there is no separate host gate daemon to stop.
  • Hard-lock the vault by destroying every stored passphrase tier — session file and persistent tiers alike — so nothing can auto-unlock it afterwards. Recovery is by re-supplying the escrowed recovery passphrase (see _stop_vault).

Phase 2 (optional, stop_containers=True): SIGKILL each container via podman stop --time 0. Containers are not removed.

Token revocation is deliberately excluded — it is irreversible and shields + dead supervisors already cut access.

logger = logging.getLogger(__name__) module-attribute

PanicResult(shields_raised=list(), shield_errors=list(), supervisors_killed=list(), supervisor_errors=list(), vault_stopped=False, vault_error=None, containers_stopped=list(), container_stop_errors=list(), shield_bypassed=False, total_running=0) dataclass

Outcome of an execute_panic invocation.

shields_raised = field(default_factory=list) class-attribute instance-attribute

shield_errors = field(default_factory=list) class-attribute instance-attribute

supervisors_killed = field(default_factory=list) class-attribute instance-attribute

supervisor_errors = field(default_factory=list) class-attribute instance-attribute

vault_stopped = False class-attribute instance-attribute

vault_error = None class-attribute instance-attribute

containers_stopped = field(default_factory=list) class-attribute instance-attribute

container_stop_errors = field(default_factory=list) class-attribute instance-attribute

shield_bypassed = False class-attribute instance-attribute

total_running = 0 class-attribute instance-attribute

has_errors property

Return whether any operation failed.

execute_panic(*, stop_containers=False)

Execute the full panic sequence.

Discovers every running container, then (in parallel) raises shields, kills the per-container supervisors (which also stops each container's embedded gate), and destroys every stored copy of the vault passphrase (hard-lock). If stop_containers, also SIGKILLs the containers afterwards (they are not removed).

Source code in src/terok/lib/domain/panic.py
def execute_panic(
    *,
    stop_containers: bool = False,
) -> PanicResult:
    """Execute the full panic sequence.

    Discovers every running container, then (in parallel) raises shields,
    kills the per-container supervisors (which also stops each container's
    embedded gate), and destroys every stored copy of the vault
    passphrase (hard-lock).  If *stop_containers*,
    also SIGKILLs the containers afterwards (they are not removed).
    """
    result = PanicResult()
    targets = _discover_targets()
    result.total_running = len(targets)
    result.shield_bypassed = get_shield_bypass_firewall_no_protection()

    _phase1_lockdown(result, targets)
    _write_panic_lock()

    # Phase 2: optional container kill
    if stop_containers and targets:
        result.containers_stopped, result.container_stop_errors = _stop_containers(targets)

    return result

panic_stop_containers()

Discover and SIGKILL all running containers (Phase 2 standalone).

Source code in src/terok/lib/domain/panic.py
def panic_stop_containers() -> tuple[list[str], list[tuple[str, str]]]:
    """Discover and SIGKILL all running containers (Phase 2 standalone)."""
    return _stop_containers(_discover_targets())

is_panicked()

Return whether the panic lock file exists.

Source code in src/terok/lib/domain/panic.py
def is_panicked() -> bool:
    """Return whether the panic lock file exists."""
    return (core_state_dir() / _LOCK_FILENAME).is_file()

clear_panic_lock()

Remove the panic lock file if it exists.

Source code in src/terok/lib/domain/panic.py
def clear_panic_lock() -> None:
    """Remove the panic lock file if it exists."""
    (core_state_dir() / _LOCK_FILENAME).unlink(missing_ok=True)

format_panic_report(result)

Format a human-readable summary of the panic result.

Source code in src/terok/lib/domain/panic.py
def format_panic_report(result: PanicResult) -> str:
    """Format a human-readable summary of the panic result."""
    sup = f"Supervisors killed: {len(result.supervisors_killed)}"
    if result.supervisor_errors:
        sup += f" ({len(result.supervisor_errors)} failed)"
    lines = [
        f"Containers found: {result.total_running}",
        _format_shield_status(result),
        sup,
        f"Vault: {'passphrase destroyed (re-supply to unlock)' if result.vault_stopped else 'FAILED'}",
    ]

    if result.containers_stopped:
        lines.append(f"Containers killed: {len(result.containers_stopped)}")

    if result.has_errors:
        lines += ["", "Errors:", *_format_errors(result)]

    return "\n".join(lines)