콘텐츠로 이동

OTP 게이팅 및 비상 정지

민감한 작업에 대한 TOTP 게이팅과 OTP 게이팅 재개 기능이 포함된 비상 정지 레벨 설명.

Revka는 고위험 에이전트 작업을 위한 두 가지 상호 보완적인 제어 수단을 제공합니다. OTP 게이팅([security.otp])은 민감한 도구와 도메인을 게이팅하도록 설정하는 TOTP 기반 두 번째 인증 요소입니다 — 현재 구현에서는 OTP 검증이 비상 정지 재개 기능에 연결되어 있습니다. 비상 정지([security.estop])는 킬 스위치입니다. 재시작 후에도 정지 상태를 유지하며, 원하는 경우 동일한 OTP를 통해서만 재개할 수 있습니다.

OTP 게이팅은 에이전트가 대부분의 경우 자율적으로 동작하되, 정해진 작업(셸, 파일 쓰기, 금융 도메인 등)에서는 사람의 확인을 거치도록 하고 싶을 때 사용하십시오. 비상 정지는 무언가 잘못되었거나 잘못될 가능성이 있을 때, 지금 당장 에이전트를 멈추고 의도적으로 해제해야 할 때 사용하십시오.

두 기능 모두 기본적으로 비활성화되어 있습니다. 이 페이지에서는 등록, 설정, 네 가지 estop 레벨, OTP 게이팅 재개 흐름, 그리고 이 모든 것을 뒷받침하는 fail-closed 설계를 설명합니다.

OTP 게이팅은 표준 TOTP(RFC 6238) 코드를 사용합니다 — 모든 인증 앱이 생성하는 것과 동일한 방식이므로 Google Authenticator, 1Password, Aegis 등 어떤 TOTP 앱이든 두 번째 인증 요소로 사용할 수 있습니다. 현재 구현에서는 OTP 검증이 비상 정지 재개 기능에 연결되어 있습니다(OTP 게이팅 재개 참조). gated_actions, gated_domains, gated_domain_categories 설정 키는 시작 시 파싱 및 검증되지만, 런타임에서 도구나 도메인 접근을 아직 게이팅하지는 않습니다.

  • 최초 사용 시 20바이트 시크릿이 생성되고, base32로 인코딩되어 시크릿 저장소를 통해 암호화된 후 ~/.revka/otp-secret0600 권한으로 기록됩니다.
  • 코드는 6자리입니다. 검증 시 현재 30초 스텝 및 앞뒤 한 스텝씩(±1)을 허용하여 Revka와 기기 간의 시계 오차를 수용합니다.
  • 검증에 성공한 코드는 cache_valid_secs 동안 캐시되므로, 단일 코드로 짧은 시간 동안 연속된 게이팅 작업을 매번 재입력 없이 승인할 수 있습니다. 만료된 캐시 항목은 다음 검증 시 정리됩니다.
  • 시크릿은 유지되므로 데몬을 재시작해도 재등록이 필요하지 않습니다.

OTP를 활성화한 상태에서 시크릿 파일이 아직 없으면 Revka가 시크릿을 생성하고 otpauth:// 등록 URI를 한 번 출력합니다. 인증 앱으로 해당 URI를 스캔하십시오.

  1. ~/.revka/config.toml에서 OTP를 활성화합니다:

    [security.otp]
    enabled = true
  2. 데몬을 시작합니다. 최초 실행 시 Revka가 다음 형식의 등록 URI를 출력합니다:

    otpauth://totp/Revka:revka?secret=BASE32SECRET&issuer=Revka&period=30
  3. TOTP 앱으로 URI(또는 QR 코드)를 스캔합니다. 이후 앱에서 token_ttl_secs초마다 새로운 6자리 코드가 생성됩니다.

[security.otp]
enabled = true
method = "totp"
token_ttl_secs = 30
cache_valid_secs = 300
gated_actions = ["shell", "file_write", "browser_open", "browser"]
gated_domains = ["*.chase.com", "accounts.google.com"]
gated_domain_categories = ["banking"]
challenge_max_attempts = 3
타입기본값설명
enabledboolfalseOTP 게이팅 마스터 스위치.
methodstring"totp"OTP 방식. 현재는 totp만 구현되어 있으며, pairingcli-prompt는 향후 사용을 위해 예약되어 있습니다.
token_ttl_secsu6430TOTP 시간 스텝(주기, 초 단위). 0보다 커야 합니다.
cache_valid_secsu64300검증된 코드가 유효한 상태로 유지되는 시간(초). token_ttl_secs 이상이어야 합니다.
gated_actionslist["shell", "file_write", "browser_open", "browser"]OTP가 필요한 도구/액션 이름 목록. 각 항목은 영숫자, _, -만 허용됩니다.
gated_domainslist[]OTP가 필요한 도메인 패턴 목록. * 와일드카드를 지원합니다(예: *.chase.com).
gated_domain_categorieslist[]게이팅 도메인으로 확장되는 프리셋 카테고리 이름: banking, medical, government, identity_providers.
challenge_max_attemptsu323챌린지 잠금 전 허용되는 최대 OTP 실패 횟수. 0보다 커야 합니다.

민감한 도메인을 일일이 나열하는 대신, gated_domain_categories에 프리셋을 하나 이상 설정하면 Revka가 해당 카테고리를 게이팅 도메인 집합으로 확장합니다:

카테고리포함 범위
banking금융 및 은행 도메인
medical의료 및 진료 기록 도메인
government정부 서비스 도메인
identity_providersID 제공자 / SSO 로그인 도메인

카테고리와 명시적 gated_domains는 합산 적용됩니다 — 둘 다 함께 사용하십시오. 알 수 없는 카테고리 이름은 시작 시 거부됩니다.

비상 정지는 운영자의 킬 스위치입니다. 비상 정지를 작동하면 명시적으로 재개하기 전까지 데몬 재시작 후에도 유지되는 상태 파일이 기록됩니다. 현재 구현에서는 지속된 상태를 revka estop status로 확인할 수 있지만, 에이전트 루프, 도구 실행 경로, 네트워크 레이어에서 작업 전에 아직 참조하지는 않습니다. 에이전트가 제어 불능 상태가 되었을 때 가장 먼저 사용해야 할 수단입니다.

Estop은 전부 아니면 전무 방식이 아닙니다. 동결 범위를 선택할 수 있으며, 레벨은 중첩됩니다 — 여러 레벨을 동시에 적용하면 제한이 쌓이고, 하나씩 개별적으로 해제할 수 있습니다.

레벨CLI 레벨 이름의도된 효과 (상태 유지됨; 런타임 적용 미구현)
전체 중단kill-all모든 에이전트 실행을 중단할 예정입니다.
네트워크 차단network-kill모든 외부 네트워크 접근을 차단할 예정입니다.
도메인 차단domain-block특정 도메인을 차단할 예정입니다. 와일드카드 패턴을 지원합니다(예: *.chase.com).
도구 동결tool-freeze이름으로 특정 도구를 동결할 예정입니다.

차단된 도메인 및 동결된 도구 목록은 쓰기 시 정규화됩니다(소문자 변환, 공백 제거, 중복 제거, 정렬). 도메인 패턴은 정책 엔진의 다른 곳과 동일한 방식으로 검증되며, 도구 이름은 영숫자, _, -만 허용됩니다.

Terminal window
# 적용 (kill-all이 기본 레벨)
revka estop
revka estop --level network-kill
revka estop --level domain-block --domain "*.chase.com"
revka estop --level tool-freeze --tool shell
# 현재 상태 확인
revka estop status
# 재개 — 특정 레벨 해제, 또는 kill-all 해제
revka estop resume # kill-all 해제
revka estop resume --network # 네트워크 차단 해제
revka estop resume --domain "*.chase.com" # 특정 도메인 차단 해제
revka estop resume --tool shell # 도구 동결 해제
revka estop resume --otp 123456 # OTP가 필요한 경우 코드 입력

레벨은 중첩되므로, 하나를 해제해도 나머지는 그대로 유지됩니다. 예를 들어 도메인 차단을 재개해도 네트워크 차단은 명시적으로 해제하기 전까지 계속 적용됩니다.

[security.estop]
enabled = true
state_file = "~/.revka/estop-state.json"
require_otp_to_resume = true
타입기본값설명
enabledboolfalse비상 정지 제어 마스터 스위치.
state_filestring"~/.revka/estop-state.json"지속적인 estop 상태가 저장되는 경로. 틸드(~) 확장을 지원하며, 상대 경로는 설정 디렉터리를 기준으로 해석됩니다. 비워둘 수 없습니다.
require_otp_to_resumebooltruetrue로 설정하면 estop 레벨을 해제할 때 유효한 OTP 코드가 필요합니다.

상태는 JSON으로 직렬화되어 원자적으로 기록됩니다(임시 파일 + 이름 변경, Unix에서 0600 권한):

{
"kill_all": false,
"network_kill": false,
"blocked_domains": ["*.chase.com"],
"frozen_tools": ["shell"],
"updated_at": "2026-06-18T10:00:00Z"
}

일반적으로 이 파일을 직접 편집할 필요가 없습니다 — 검증과 정규화가 실행되도록 revka estop을 사용하십시오. 런타임은 kill_all이 설정되어 있거나, network_kill이 설정되어 있거나, 두 목록 중 하나라도 비어 있지 않으면 estop이 적용 중인 것으로 판단합니다.

두 기능은 재개 시점에서 연결됩니다. [security.estop].require_otp_to_resume = true로 설정하면 유효한 OTP 코드 없이는 어떤 estop 레벨도 해제할 수 없습니다:

  1. 에이전트가 제어 불능 상태가 되었거나 그럴 가능성이 있습니다. 정지를 적용합니다:

    Terminal window
    revka estop --level kill-all
  2. 나중에 재개를 결정합니다. 코드 없이 재개를 시도하면 거부됩니다:

    Terminal window
    revka estop resume
    # error: OTP code is required to resume estop state
  3. 인증 앱에서 현재 코드를 확인하고 입력합니다:

    Terminal window
    revka estop resume --otp 123456

    코드는 액션 게이팅에 사용되는 것과 동일한 TOTP 시크릿으로 검증됩니다(±1 스텝 허용). 잘못되거나 비어 있는 코드는 거부되고(Invalid OTP code; estop resume denied) 정지 상태가 유지됩니다.

비상 정지는 안전하게 실패하도록 설계되었습니다. 상태 파일이 존재하지만 읽거나 파싱할 수 없는 경우 — 손상, 잘림, 잘못된 수동 편집 등 — Revka는 이를 무시하고 계속 실행하지 않습니다. 대신 fail-closed 상태로 진입합니다: kill_all = true. 손상된 킬 스위치는 “계속 진행”이 아닌 “모두 중단”으로 처리됩니다.

  • 상태 파일이 없으면 정지가 적용되지 않은 것으로 간주합니다(정상적인 초기 상태).
  • 읽을 수 없거나 파싱할 수 없는 상태 파일은 kill_all = true를 의미하며, fail-closed 상태가 다시 기록되어 revka estop status에서 명시적으로 확인할 수 있습니다.
  • fail-closed 상태는 유지되며 revka estop status에서 확인할 수 있습니다. 현재 게이트웨이 요청은 estop 상태에 의해 게이팅되지 않지만, 운영자가 명시적으로 정지를 확인하고 재개할 수 있습니다.

이는 모호한 보안 상태는 안전한 선택으로 해석된다는 Revka의 광범위한 원칙을 반영합니다. OTP 게이팅 재개와 결합하면, 제어 불능이 되거나 변조된 에이전트가 조용히 스스로를 다시 온라인 상태로 만들 수 없습니다.