
이와토 게이트(岩戸ゲート): AI 에이전트에게 '운영의 자율성'을 부여하면서도, 임의 발신을 구조적으로 불가능하게 만든 과정
요약
AI 코딩 에이전트 AMA-teras의 보안 설계인 '이와토 게이트'를 소개합니다. 에이전트가 승인 없이 임의로 외부 발신이나 코드 병합을 수행하지 못하도록, 실행 함수를 WeakMap에 봉인하여 구조적으로 우회 경로를 차단하는 방어 메커니즘을 다룹니다.
핵심 포인트
- 실행 함수(executor)를 WeakMap에 봉인하여 외부 참조를 원천 차단
- 승인 UI 우회를 방지하기 위해 코드상에 승인 없는 실행 경로를 제거
- 선언된 능력과 실제 구현된 executor의 일치성을 등록 시 검증
- 자기 진화하는 에이전트의 운영 자율성과 보안 사이의 균형 설계
서론: Star 0 프로젝트가 보안 설계에 대해 이야기하는 이유
먼저 현재의 수치를 솔직하게 말씀드리겠습니다.
- GitHub Star:
0 - registry 클론:
10 - Zenn 좋아요:
2 - 공개 플러그인:
1건
아무도 보지 않는 규모의 프로젝트가 굳이 보안 설계에 대한 글을 쓰는 이유가 무엇일까요? 이유는 간단합니다. 숫자가 작은 지금 이 시기에, 신뢰의 기반이 될 '방어 메커니즘의 실화'를 남기고 싶기 때문입니다.
AMA-teras는 Electron으로 만든 데스크톱 AI 코딩 에이전트입니다. 대화를 통해 계획 → 구현 → 디버깅 → 테스트를 진행하며, 부족한 기능을 발견하면 스스로 도구를 생성하여 통합(자기 진화)하는 설계를 가지고 있습니다. 생성물은 격리된 git worktree에서 타입 체크, 테스트, 실제 구동 스모크 검증 게이트를 거치며, 본체로 승격하려면 반드시 인간의 승인을 거칩니다.
자기 진화하는 에이전트에게 '프로젝트 운영'까지 맡기게 된다면, 질문받는 것은 기능 자체가 아니라 방어 메커니즘입니다. 이 글은 그 방어 메커니즘 = 이와토 게이트를 '승인 플로우가 있습니다' 이상의 수준으로 만들기 위해 무엇을 했는지, 그리고 실수할 뻔했던 이야기의 기록입니다.
문제 설정: AI에게 운영을 맡길 때 두려운 것은 무엇인가
최악의 시나리오는 구체적입니다.
- 에이전트가
임의로 SNS에 게시하는 것 - 에이전트가
임의로 PR을 병합(merge)하는 것
이러한 사고에 대해 '승인 플로우를 만들었습니다'라고만 말해서는 부족합니다. 승인 UI 바로 앞의 함수를 직접 호출할 수 있다면, 승인 플로우는 그저 장식품일 뿐입니다. LLM이 생성한 코드나, 미래의 자신(자기 진화로 추가되는 도구)이 해당 함수에 도달할 수 있는 한, 우회 경로는 계속 존재합니다.
필요했던 것은 '승인을 거쳐 주세요'라는 부탁이 아니라, 승인 없이 실행에 도달하는 경로가 코드상에 존재하지 않는 것이었습니다.
이와토 게이트: 승인 없이 실행에 도달하는 경로를 코드에서 지우다
외부로의 발신 및 쓰기는 모두 이와토 게이트(승인 다이얼로그)를 거칩니다. 그 구현 과정에서 중점을 둔 세 가지 포인트입니다.
executor를 WeakMap에 봉인하다
실행 함수(executor)는 등록 시 이와토 게이트 내부의 WeakMap에 봉인하며, 호출원(calling source)이 가지고 있던 참조에서도 삭제합니다.
// 개념도: 등록 후, 게이트 외부에 executor에 대한 참조가 남지 않음
register(adapter: Adapter) {
executorVault.set(adapter, adapter.execute); // WeakMap에 봉인
...
}
이후로 executor를 건드리는 것은 게이트 내부뿐이며, 그곳에는 '미리보기 표시 → 인간의 승인'이 반드시 끼어듭니다. 미리보기와 승인을 거치지 않고 실행하는 경로가 애초에 참조로서 존재하지 않게 만든 것입니다.
일괄 승인(M39)을 추가했을 때도 이 원칙은 느슨해지지 않았습니다. 승인 다이얼로그는 1회지만 전체 건의 전문 미리보기를 나열하고, 미승인이라면 executor는 단 하나도 움직이지 않습니다. 실행과 감사(audit)는 각각 진행되며, 1건이 실패해도 나머지를 계속 진행하여 'N건 중 M건 성공'을 반환합니다 (부분 성공을 성공으로 오해하게 만들지 않음).
선언과 구현의 일치를 등록 시 검사하다
어댑터가 선언하는 능력과 실제로 등록되는 executor의 일치 여부를 등록 시에 검사합니다.
여기서 상징적인 것이 Bluesky를 다룬 경우입니다. '게시할 수 있습니다'라고 선언해 놓고 실행 시 미구현 에러를 반환하는 방식은 취하지 않았습니다. 선언 자체를 비워둡니다. 선언과 실제 동작이 어긋난 상태를 코드 베이스에 두지 않으며, 대응할 때는 executor와 선언을 동시에 추가한다는 방침입니다.
같은 이유로 X / 하테부(はてブ)는 이와토 게이트를 통과하지 않습니다. 어댑터의 execute 선언이 비어있기(규약) 때문에 앱은 아무것도 발행하지 않고, URL만 반환하여 게시 화면을 열 뿐입니다. 마지막 클릭은 인간입니다.
거부까지 포함해 전부 기록(audit.jsonl)
승인된 실행뿐 아니라, 거부를 포함해서 모두 audit.jsonl에 추가합니다.
{
또한, 승인 요청에는 **10분의 타임아웃 = 거부 처리**를 설정했습니다. 사람이 자리를 비운 사이에 실행이 보류된 상태로 계속 쌓이는 상황을 방지하기 위해서입니다. 무인 방치는 '보류'가 아니라 '거부'로서 기록에 남습니다.
## 신의(神議) 아키텍처: 자율의 범위를 2단계제로 구분
운영 측에서는 여러 역할('신(神)'이라 부릅니다. 관측·순회·초안 생성·트리아지(Triage) 등)이 스케줄에 따라 실행되며, 정기적으로 '신의(神議)'를 통해 운영 방침을 제안합니다. 여기서 **자율적으로 해도 되는 것**과 **승인이 필요한 것**을 2단계로 구분했습니다.
### 자율적으로 할 수 있는 것 · 승인이 필요한 것
| 자율적으로 할 수 있는 것 | 인간의 승인이 필요한 것 |
|---|---|
| 실행 간격 조정 | 새로운 툴의 추가 |
| ... | 예산 인하 |
| 신(神)의 신설·삭제 | |
방향성이 명확합니다. **축소되는 방향·멈추는 방향은 자율, 확장되는 방향·강해지는 방향은 승인**입니다.
### '기만적 증액' 대책: 선언이 아닌 실측값으로 재분류
우회로로 예상했던 것은 "예산을 인하하겠습니다"라고 선언하고 실제로는 인상하는, 이른바 기만적 증액입니다. 대책은 선언을 신뢰하지 않는 것입니다. **제안의 선언 라벨이 아니라, 변경 전후의 실측값을 비교하여 인하/인상을 재분류**합니다. 실측값이 인상이라면, 선언이 무엇이든 승인 단계로 넘어갑니다.
### 신(神)의 신설은 이중 승인 + 테스트로 고정
신(神)의 신설은 가장 영향이 큰 조작이므로 이중 승인 방식으로 했습니다. 나아가 "승인을 거부했을 때 상태가 바뀌지 않는 것" **그 자체를 테스트로 고정**했습니다. 가드는 구현한 순간보다, 이후의 변경으로 인해 조용히 망가지는 것이 더 무섭습니다. 회귀 테스트(Regression Test)로 만들어 두면, 미래의 자신(과 자기 진화 잡(Job))이 망가뜨렸을 때 즉시 빨간색(에러)으로 표시됩니다.
## 아차 했던 순간들 (숨기지 않음)
이 프로젝트의 신뢰는 가드(Guard)의 실화로만 성립할 수 있기에, 실수한 부분도 적습니다.
### `.claude/settings.json`을 리포지토리에 커밋함
개발에 사용하던 Claude Code의 설정 파일 `.claude/settings.json`을 개인용 설정을 포함한 채로 리포지토리에 커밋했습니다. 이 파일은 **clone한 모든 사람의 Claude Code에 적용됩니다**. 에이전트의 안전 설계를 논하는 리포지토리가, clone 대상 개발 환경의 동작을 의도치 않게 바꾸고 있었던, 웃지 못할 이야기입니다.
대응책으로 개인 설정은 `settings.local.json`으로 옮기고, 리포지토리 측에는 **안전 측면의 deny 설정만 포함된 최소 구성**만 남겼습니다.
### QR 코드가 말없이 사라진 버그
원격 접속용 QR 코드가 어느 날 UI에서 말없이 사라졌습니다. 원인은 localStorage 키의 이름 변경 후, **이전 키만을 참조하는 폴백(Fallback)이 남아 있었기** 때문입니다. 새 키로 저장된 데이터를 읽지 못했고, 에러도 발생시키지 않은 채 비표시 상태가 되었습니다.
수정과 동시에 결정한 것이 "**말없이 사라지지 않는다**"라는 UI 원칙입니다. 표시할 수 없다면 이유를
### Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기