OpenAI Codex · Code Mode

승인 정책: 같은 명령, 물을지 말지는 노브 두 개

기본으로 물을지는 샌드박스 종류에 묶여 있어요. 한 번 Yes를 누른 뒤, 기억하는 것은 전체 명령일까요, 접두일까요.

강의 목표이 레슨을 마치면 두 가지를 말할 수 있어요. 같은 명령이 왜 바로 통과에서 사람이 고개를 끄덕여야 하는 쪽으로 바뀌는지. 그리고 Yes를 한 번 누른 뒤 다음에 또 물을지, 시스템이 도대체 무엇을 기억했는지.
먼저 해보기 · 같은 명령, 정책을 바꿔 결말을 봅니다
정책 넷, 샌드박스 셋: 어느 칸이 허용·질문·거절 도장을 찍는지
명령
앞 세 줄은 Allow. 넷째는 execpolicy Prompt에 맞습니다.
정책
샌드박스
노브
칸을 눌러도 그쪽으로 점프합니다. 기억 방식 둘은 서로 배타적이에요.
열두 칸 모래판 · 읽기 전용과 작업 공간은 종종 같은 도장Restricted 두 행이 부딪힙니다
CodexOnRequest · workspace-write
대기재생을 눌러, 같은 명령이 정책을 바꾼 뒤 어떤 도장을 받는지 보세요.
모델에게 주는 설명서정책이 아직 고르지 않았어요.
DSHask · workspace-write
대기노브 둘은 독립입니다. 전체 디스크 쓰기여도 계속 물을 수 있어요.
무엇을 기억하는가allowed-once뿐입니다. 한 번 물어도, 다음에는 또 물어요.
논리 궤적 · 애니메이션 한 걸음이 소스 어디에 대응하는지
  1. 이번 turn의 AskForApproval을 읽습니다protocol.rs L924
  2. FileSystemSandboxKind가 Restricted인지 봅니다permissions.rs L227
  3. Never는 Skip, UnlessTrusted는 NeedsApprovalsandboxing.rs L198
  4. OnRequest나 Granular는 Restricted일 때만 묻습니다sandboxing.rs L200
  5. Granular가 물어야 하는데 샌드박스 승인을 끄면 Forbiddensandboxing.rs L209
  6. execpolicy Prompt는 두 번째 문을 한 번 더 지납니다exec_policy.rs L214
  7. 세션 캐시는 ApprovedForSession만 받고, 정확한 key를 봅니다sandboxing.rs L108
  8. 정책이 바뀌면 모델 설명서도 같이 바뀝니다permissions_instructions.rs L271
재생을 눌러, 같은 명령이 바로 통과에서 사람이 고개를 끄덕여야 하는 쪽으로 가는 길을 보세요.
열두 칸 중 몇 칸이 같은가read-only와 workspace-write는 둘 다 Restricted입니다. OnRequest는 이 두 행에 같은 질문 도장을 찍어요. danger-full-access는 kind를 Unrestricted로 비틀고, 기본 함수는 더 이상 묻지 않습니다.
Never도 거절할 수 있어요정책이 질문을 요구하면 Never는 질문을 거절로 승급합니다. sandbox_approval을 끄면, 원래 뜨려던 창만 막아요. 파일 시스템이 이미 Unrestricted면 Forbidden 분기에 들어가지 못합니다.
기억 방식 둘세션으로 npm run test를 기억한 뒤 npm run lint를 돌리면 key가 안 맞아 또 묻습니다. 접두로 기억해야 범위가 넓어져요. 일반 팝업이 기본으로 내미는 것은 접두 쪽입니다.
수업용 시연: 무대는 판정 함수와 캐시 두 층의 구조 차이만 보여 줍니다. 실제 명령은 실행하지 않아요. 논리 궤적 오른쪽 행 번호는 openai/codex 저장소 commit 4f39251a01에 대응합니다.
아이디어 1 · 기본으로 물을지는 샌드박스 종류에 묶입니다
어떤 문제를 푸는가

승인은 on-request에 두고, 샌드박스를 workspace-write에서 danger-full-access로 비틉니다. 십 분 전 그 샌드박스 밖 명령은 창이 떴어요. 지금은 안 뜹니다. 승인 노브는 안 건드렸습니다.

승인 정책 AskForApproval은 이번 turn이 쓰는 사람 질문 규칙이고, 타입에는 variant가 넷입니다. 파일 시스템 샌드박스 종류는 셋이고 기본은 Restricted, 읽기 전용이거나 작업 공간만 쓰는 뜻이에요. 노브 둘은 같은 판정 함수 안에 있습니다. 하나를 비틀면 다른 쪽 행동도 따라갑니다.출처:codex-rs/protocol/src/protocol.rs 924–947행, codex-rs/protocol/src/permissions.rs 227–232행

아이디어는 무엇인가

명령은 먼저 승인 정책을 읽고, 파일 시스템이 Restricted인지 본 뒤, execpolicy가 Allow·Prompt·Forbidden을 겹칩니다. execpolicy는 명령 텍스트에 세 상태를 주는 규칙 파일이에요.

기본으로 물을지는 default_exec_approval_requirement에 있습니다. Never 층은 Skip, UnlessTrusted 층은 NeedsApproval. OnRequest와 Granular는 Restricted일 때만 묻습니다. read-onlyworkspace-write는 둘 다 Restricted라 두 칸의 기본 결말이 같아요. danger-full-access는 kind를 Unrestricted로 바꾸고, 기본 함수는 Skip을 탑니다.출처:codex-rs/core/src/tools/sandboxing.rs 198–230행

Granular에는 문이 하나 더 있어요. 승인이 필요하고 sandbox_approval이 꺼져 있으면 바로 Forbidden. 파일 시스템이 이미 Unrestricted면 needs_approval이 먼저 거짓이 되어 Forbidden 분기에 들어가지 못합니다. 샌드박스 승인을 끄는 것은, 원래 뜨려던 창만 막는 일입니다.

Never가 execpolicy Prompt를 만나면 질문은 거절로 승급합니다. 무엇이든 달려도 된다는 뜻이 아니에요.출처:codex-rs/core/src/exec_policy.rs 214–236행

명령 도착 AskForApproval 파일 시스템 kind Restricted인가 기본 요구 Skip / 질문 / 거절 Allow면 달립니다 Prompt는 문을 한 번 더 OnRequest에 Unrestricted면 기본은 Skip. Never에 Prompt면 질문은 거절로 승급합니다.
수업용 구조도: 기본 요구를 먼저 내고, execpolicy가 한 층을 겹칩니다.

untrusted는 설정에 쓸 수 없어요. 타입은 남아 있고, 공개 문자열은 은퇴했습니다. 사용자가 명시적으로 쓰면 로드 전체가 실패해요. 안 써야 프로젝트 신뢰를 봅니다. 신뢰면 OnRequest, 명시적으로 미신뢰면 UnlessTrusted.출처:codex-rs/core/src/config/mod.rs 3607–3625행

로드 시점의 하드 거절이 하나 더 있습니다. requirements가 danger-full-access를 허용하지 않는데 설정이 approval_policy = "never"이면, 로더는 권한 프로필을 먼저 읽기 전용으로 떨어뜨립니다. 읽기 전용에 질문 안 함은, 모델이 좁은 샌드박스에 있는데 물을 사람이 없다는 뜻이에요. 그 조합은 바로 불법입니다.출처:codex-rs/core/src/config/mod.rs 3969–3979행

왜 오래가는가

전체 디스크가 쓰일 때 샌드박스 밖을 한 번 더 묻는 것은 의미가 없어요. 노브 둘을 묶으면 팝업이 줄어듭니다. 대가는 샌드박스를 바꾸면 승인 행동이 조용히 따라간다는 점이에요. 다른 언어로 다시 써도 먼저 답해야 합니다. 전권 모드에서도 사람을 물을까요.

정책 enum은 분기를 남김없이 써서 규칙을 말합니다. variant를 하나 더하면 컴파일러가 모든 판정 함수에 입장을 강요해요. 타입이 런타임의 문을 지키는 겁니다.

아이디어 2 · 물은 뒤에, 무엇을 기억하는가
어떤 문제를 푸는가

팝업의 기억 두 줄은 닮아 보여요. 하나는 이 세션에서 이 명령을 기억하는 것. 다른 하나는 접두를 execpolicy에 써서 세션을 넘기는 것. 잘못 누르면 뒤의 경계 문제가 반대로 나옵니다.

일반 exec 팝업 기본 메뉴에는 ApprovedForSession조차 없어요. 네트워크 컨텍스트가 있을 때만 붙습니다. 보통 명령은 한 번 승인, 접두 수정 제안이 있으면 접두 기억을 더하고, 마지막은 취소. 사용자가 가장 자주 만나는 기억은 접두 쪽입니다.출처:codex-rs/protocol/src/approvals.rs 314–347행

아이디어는 무엇인가

세션 캐시는 ApprovalStore에 살고, 세션과 수명이 같습니다. key는 정규화된 전체 명령에 cwd, 환경, execpolicy 지문을 더한 것이에요. 모든 key가 이미 ApprovedForSession일 때만 맞습니다. 한 번 통과인 Approved는 map에 들어가지 않아요.출처:codex-rs/core/src/tools/sandboxing.rs 64–116행

npm run test를 세션에 승인하면 npm run lint는 다른 key입니다. /bin/bash -lcbash -lc는 단일 평문 명령을 쪼갤 수 있을 때 같은 token 묶음으로 캐시돼요. 세션 캐시는 전체 명령을 보고, npm *는 보지 않습니다. 접두 확장은 execpolicy로만 갑니다.

승인 정책을 바꿔도 이 map은 비우지 않아요. key에 AskForApproval이 없습니다. 도중에 OnRequest에서 UnlessTrusted로 바꿔도 이미 캐시된 승인은 유효해요. execpolicy를 바꿔야 지문이 바뀌고, 그때야 캐시가 죽습니다.

사용자가 결정을 누름 ReviewDecision ApprovedForSession 정확한 key가 세션 서랍에 ApprovedExecpolicyAmendment 접두를 규칙 파일에 씀 다음에 전체 명령이 같으면 그때야 팝업을 건너뜁니다 접두가 맞으면 통과 세션을 넘기고, 범위는 더 넓어요 Approved는 이번만 통과하고 map에 안 들어갑니다. npm run test와 npm run lint는 key 둘입니다. 일반 exec 기본 메뉴는 종종 접두 한 줄만 내밉니다.
수업용 대조: 한 층은 전체 명령을 보고, 한 층은 접두를 보고 디스크에 씁니다.
왜 오래가는가

기억 방식 둘은 위협 둘에 대응합니다. 정확한 key는 인자 교체를 막지 못해요. 접두는 막지만, 포장 명령의 범위를 너무 넓히기도 쉽습니다. 층을 나눠야 각자 금지 조언 명단을 달 수 있어요. 하루 팝업이 많지 않으면, 한 번만 통과시켜도 성립합니다.

아이디어 3 · 정책이 바뀌면 설명서도 같이 바뀝니다
어떤 문제를 푸는가

정책만 바꾸고 런타임 분기만 고치면 부족해요. 모델이 보는 설명도 같이 바뀌어야 합니다. 안 그러면 옛 규칙으로 require_escalated를 또 달라고 해요. Never 아래에서 설명서가 여전히 승격을 가르치면, 런타임은 질문을 거절로 승급하고 모델은 벽만 반복해서 부딪힙니다.

아이디어는 무엇인가

권한 설명은 developer 메시지 한 토막입니다. 샌드박스 템플릿을 고르고, 승인 템플릿을 고릅니다. Never, UnlessTrusted, OnRequest는 각각 준비된 마크다운이 있어요. Never 파일은 한 줄입니다. sandbox_permissions를 다시 주지 마세요, 명령은 거절됩니다. Granular에는 다섯 번째 파일이 없고, 스위치 다섯으로 허용·거절 목록을 그 자리에서 붙입니다. 설명 두 벌을 한 토막에 붙여, 모델이 현재 조합을 한 번에 봅니다.출처:codex-rs/prompts/src/permissions_instructions.rs 271–291행

훅이 사람보다 앞에 있어요. 훅이 Allow나 Deny를 주면 팝업은 안 뜹니다. 훅의 Allow는 한 번의 Approved이고, 세션 캐시에는 쓰지 않아요.

정책이 바뀌면 설명서도 같이 바뀌어야 합니다.
왜 오래가는가

런타임과 모델 설명서는 같은 계약의 양면입니다. 판정 함수를 바꾸면 사전 그 줄도 같이 바꿔야 해요. 둘을 같은 모듈에 두고 같은 테스트로 양쪽에 먹이면, 다른 언어로 옮겨도 됩니다.

가로 비교 · 노브는 독립일까요, 묶여 있을까요

DSH: 노브 둘, 한 번 승인은 넓히지 않음

DSH 승인 정책은 asknever뿐입니다. never는 답하는 쪽에 넘기기 전에 rejected를 돌려주고, 뒤에 걸린 리스너는 그 약속을 못 바꿉니다. 승인 결과는 allowed-once뿐이에요. 세션 캐시도, 접두 수정도 없습니다. 한 번 물어도 다음에는 또 물어요.출처:packages/interaction/user-approval/src/index.ts 84–94행, 304–312행

샌드박스와 승인은 독립 노브 둘입니다. 사용자가 보는 드롭다운은 프리셋 표예요. workspace-writeask에, danger-full-accessnever에 묶입니다. 다음 단을 누르면 setter 둘을 따로 호출하고, 표에 안 맞으면 custom을 보여 줍니다. 그래서 DSH는 승인을 ask에 두고 샌드박스만 전체 디스크 쓰기로 비틀 수 있어요. Codex의 OnRequest는 Unrestricted에서 기본이 Skip이라, 그 조합은 기본 함수 안에서 따로 존재할 수 없습니다.출처:packages/interaction/permission-presets/src/index.ts 167–176행

양쪽 모두 소스 대조 완료 · 2026-08-22 · DSH · 승인과 권한

Claude Code: 기억을 규칙 표에 씁니다

Claude Code의 대외 권한 모드는 다섯 단이고, 내부에 autobubble이 더 있어요. 규칙 출처는 userSettings, projectSettings, session, cliArg. 판정은 도구 전체 deny를 먼저 보고 아래로 내려갑니다.출처:restored-src/src/types/permissions.ts 16–29행, restored-src/src/utils/permissions/permissions.ts 1169–1181행

alwaysAllowRules는 출처별로 허용을 적을 수 있고, session은 그중 한 단입니다. 다음에는 규칙으로 맞추며, 전체 argv가 같을 필요는 없어요. 대가는 매칭 함수가 포장을 스스로 막아야 한다는 점입니다. Codex는 범위 확장을 execpolicy 접두와 금지 조언 명단에 맡깁니다. dontAsk는 Never에 가깝습니다. Codex에는 공개된 전면 허용 승인 정책이 없고, Never도 execpolicy에 막혀요.출처:restored-src/src/types/permissions.ts 54–62행, 433행

양쪽 모두 소스 대조 완료 · 2026-08-22
수업 실습
01

샌드박스를 비틀면, 팝업은 남아 있을까요

승인은 on-request에 두고, 샌드박스를 workspace-write에서 danger-full-access로 비틉니다. 작업 공간 밖 경로를 쓰는 명령을 다시 돌리세요. 팝업은 남아 있을까요? 왜요?

이어서 같은 npm run test를 세션으로 승인한 뒤 npm run lint를 제출합니다. 캐시는 맞아야 할까요? 접두로 기억을 눌렀다면 답이 바뀔까요?

Takeaway:같은 명령을 물을지는 승인 정책과 파일 시스템이 Restricted인지에 달립니다. Never가 반드시 물어야 하는 규칙을 만나면 질문은 거절로 승급해요. 물은 뒤의 기억은 두 층입니다. 세션 층은 정확한 key를 보고, 접두는 지속 층에서만 허용됩니다. 정책이 바뀌면 모델에게 주는 그 문장도 같이 바뀌어야 해요.