DS ForgeCourses
기존 Lab

REPO ≠ RUNTIME ≠ AUTHORITY ≠ PROOF

Repo는 독립인데, 실행은 왜 연결됐나?

독립된 사이트 repo의 Codex 세션이 어떻게 Talkak의 Approvals에 제안을 만들고, 실행 중인 앱에서 승인까지 눌렀는지 실제 사건을 끝까지 추적한다. 마법도, repo 합체도 아니다. 공유된 도구·제안 통로·실행 권한 경계다.

독립 코드+공유 통로+분리 권한
VERIFIED · 직접 확인

이 세션의 파일·도구 응답·실제 UI에서 확인했다.

INFERENCE · 설계 추론

관측 사실을 바탕으로 한 권장 아키텍처다.

UNKNOWN · 아직 미검증

소스·E2E·권한 inventory가 더 필요하다.

01 · SIX DIFFERENT BOUNDARIES

Repo 폴더는 코드 경계다. 시스템 전체의 보안 경계는 아니다.

노드를 누르면 각 층이 무엇을 소유하고, 무엇을 볼 수 있고, 무엇을 보장하지 못하는지 분리해서 본다.

B3VERIFIED · 직접 확인

Proposal Control Plane

실행 전 의도 보관

소유하는 것
pending 제안, 대상, artifact 경로, 비용·rollback 설명과 proposal ID
볼 수 있는 것
MCP가 등록한 제안과 Talkak Approvals UI가 읽는 동일한 대기열
보장하지 못하는 것
제안을 저장했다는 사실만으로 외부 배포가 실행되거나 성공하지 않는다.
핵심

Codex가 Talkak repo를 몰래 읽은 것이 아니다. 이 세션에 Dalkkak MCP와 실행 중인 앱을 조작할 수 있는 별도 통로가 연결되어 있었고, Talkak UI가 같은 proposal store를 읽었다.

02 · THE ACTUAL INCIDENT

“최종 배포해줘” 한 문장이 실제로 지나간 7단계.

이것은 일반적인 이상형이 아니라 이번 세션에서 직접 관측한 trace다.

STEP 06VERIFIED · 직접 확인

Runner가 access token 부재로 거부

승인 의도와 실행 권한이 분리되어 있음을 실제 실패로 확인했다. 외부 배포는 시작되지 않았다.

FAILED BEFORE DEPLOY
VERIFIED · 직접 확인

Sites 연결에서는 기존 사이트를 읽을 수 있었지만 Talkak managed runner는 access token을 받지 못했다. 한 connector의 로그인 상태가 다른 runner에 자동 상속되지 않았다는 실제 증거다.

03 · HOOK, RULE, GATE, AUTHORITY

Hook은 문 앞 센서다. 잠긴 문 전체는 아니다.

Hook은 lifecycle event에서 코드를 실행하는 adapter다. host가 그 결과를 존중하면 특정 경로를 차단할 수 있지만, 등록되지 않은 다른 경로와 credential 자체까지 사라지게 하지는 않는다.

안내판AGENTS·system 규칙

이 문으로 가라고 알려준다.

센서Hook · Interceptor

등록된 경로에서 검사·차단한다.

결재Proposal · Approval

정확한 작업에 사람 결정을 결합한다.

자물쇠Runner · Credential

유효한 승인 없이는 권한을 주지 않는다.

UNKNOWN · 아직 미검증

Talkak 내부가 실제로 어떤 Hook API를 쓰는지는 이번 사건만으로 확인되지 않았다.

확인된 것은 proposal-only MCP 경로, durable pending 상태, Approvals UI, 승인 시도, Runner의 인증 거부다. 따라서 ‘숨은 hook 하나가 전부 막았다’고 설명하면 증거보다 앞서간다.

규칙부터 실행 권한까지: 무엇을 보장하고 무엇을 보장하지 못하는가
메커니즘분류할 수 있음못 하는 것강도
System·developer·AGENTS 지침Guidance정상 절차와 금지사항을 모델에게 알려준다.모델 오류, 컨텍스트 손실, 별도 credential 경로를 물리적으로 막지 못한다.
Skill·runbookProcedure선택된 작업을 반복 가능한 순서로 수행하게 한다.선택되지 않은 경로나 실제 권한을 통제하지 않는다.
Hook·interceptorPath-scoped control등록된 lifecycle 지점에서 allow·deny·rewrite·approval을 결정한다.그 hook을 지나지 않는 CLI·HTTP·다른 MCP·worker를 자동으로 막지 못한다.
Proposal-only tool surfaceInterface control에이전트가 직접 실행 대신 구조화된 pending 제안만 만들게 한다.에이전트가 별도 배포 credential이나 우회 도구를 보유한 상황을 해결하지 못한다.
Human approval + backend validationDecision gate사람의 결정을 정확한 proposal·대상·revision에 결합한다.모호한 화면, 변경된 artifact, replay를 추가 검증 없이 해결하지 못한다.
Runner-only credential + egress 제한Authority boundary유효한 승인 없이는 실제 외부 변경 권한을 얻지 못하게 한다.잘못된 downstream 동작이나 idempotency 결함까지 자동 해결하지 않는다.
Status·receipt·reconciliationProof무엇이 승인되고 실제로 실행됐는지 사후에 증명한다.나쁜 실행을 사전에 차단하지 않는다. 대신 재현·감사·복구 근거를 준다.

04 · BYPASS & FAILURE LAB

“항상 승인”은 정상 데모가 아니라 우회 공격으로 증명한다.

실패를 선택하면 어디에서 멈춰야 하고 어떤 테스트가 필요한지 본다.

INJECT

배포 access token이 없음

Approve는 눌렸지만 managed runner가 인증되지 않았다.

B4 · Human DecisionB5 · Privileged RunnerB6 · Status · Receipt · Reconciliation
SAFE STATE
FAILED BEFORE EFFECT · 외부 변경 없음
필요 통제
명확한 인증 오류, retry 가능한 상태, direct CLI fallback 금지
통과 테스트
token 없는 승인 실행이 PaaS 변경 0건과 감사 가능한 실패를 남기는지 확인한다.

05 · HARD-GATE REFERENCE DESIGN

모델에게 권한을 주지 말고, 승인된 Runner에게만 빌려준다.

아래는 이번 사건의 관측 사실이 아니라, 우회를 막기 위한 공급자 중립 권장 설계다.

INFERENCE · 설계 추론APPROVAL-BOUND EXECUTOR
type ProposalEnvelope = {
  proposalId: string;
  projectId: string;
  target: string;
  payloadDigest: string;
  artifactDigest: string;
  risk: "low" | "medium" | "high";
  expiresAt: string;
};

type ApprovalGrant = {
  proposalId: string;
  payloadDigest: string;
  approverId: string;
  decision: "approved" | "rejected";
  nonce: string;
  expiresAt: string;
};

async function executeApproved(
  proposal: ProposalEnvelope,
  grant: ApprovalGrant,
) {
  verifyGrant(grant);
  assert(grant.proposalId === proposal.proposalId);
  assert(grant.payloadDigest === proposal.payloadDigest);
  assertNotExpired(proposal, grant);
  await verifyArtifactDigest(proposal.artifactDigest);

  const claim = await executions.claimOnce({
    proposalId: proposal.proposalId,
    idempotencyKey: proposal.payloadDigest,
  });
  if (!claim.acquired) return claim.previousResult;

  const credential = await credentialBroker.issueScoped({
    projectId: proposal.projectId,
    target: proposal.target,
  });

  try {
    const result = await adapter.execute(proposal, credential);
    const verified = await adapter.verify(result);
    return receipts.recordExecuted(proposal, grant, verified);
  } catch (error) {
    if (couldHaveCompletedExternally(error)) {
      return executions.markUnknownOutcome(proposal.proposalId);
    }
    return receipts.recordFailedBeforeEffect(proposal, grant, error);
  }
}
STATE MACHINE
  1. DRAFT → PENDING
  2. PENDING → REJECTED | EXPIRED | APPROVED
  3. APPROVED → EXECUTING
  4. EXECUTING → EXECUTED | FAILED_BEFORE_EFFECT | UNKNOWN_OUTCOME
  5. UNKNOWN_OUTCOME → RECONCILING → EXECUTED_CONFIRMED | NOT_EXECUTED_CONFIRMED

금지: REJECTED→EXECUTING, EXPIRED→EXECUTING, UNKNOWN_OUTCOME→blind retry, digest 변경 후 옛 승인 재사용.

ROOT OF TRUST

Hook도 UI도 아니다.

모든 외부 변경 경로가 승인 검증 Runner로 수렴하고, 그 Runner만 최소 권한 credential을 소유하는 구조가 신뢰의 뿌리다.

06 · TEST THE GUARANTEE

이 테스트를 통과하기 전에는 “무조건 승인”이라고 말하면 안 된다.

  1. 01

    제안 생성은 로컬 pending 상태만 만들고 외부 network call은 0건이다.

  2. 02

    승인 grant가 없거나 만료·거절·scope 불일치면 Runner가 실행하지 않는다.

  3. 03

    승인 후 payload·target·artifact digest가 바뀌면 이전 승인은 무효다.

  4. 04

    동시 승인과 retry에서도 외부 효과는 최대 한 번이다.

  5. 05

    직접 CLI·SDK·HTTP·다른 MCP 경로는 같은 정책과 권한 경계에서 차단된다.

  6. 06

    인증 실패는 외부 변경 없이 FAILED_BEFORE_EFFECT와 복구 안내를 남긴다.

  7. 07

    외부 성공 여부가 불명확하면 blind retry 대신 UNKNOWN_OUTCOME으로 간다.

  8. 08

    proposal·approval·execution·provider status·receipt가 같은 digest를 가리킨다.

  9. 09

    AI가 지침을 잊는 eval에서도 승인 없는 실제 변경은 0건이다.

  10. 10

    사람이 승인 전에 대상·효과·비용·rollback을 정확히 설명할 수 있다.

FUNCTION

기능 검증

제안·승인·거절·인증 실패·우회 차단·단 한 번의 실행·receipt 일치를 실행한다.

QUALITY

품질 검증

사람이 대상·효과·위험·rollback을 승인 전에 오해 없이 이해하는지 측정한다.

PRODUCT

워크플로 검증

한 번의 명확한 결정으로 실제 작업이 완료되고 실패 시 복구되는지 확인한다. 이번 사건은 token 오류 때문에 아직 미검증이다.

07 · EVIDENCE LEDGER

확인한 것, 추론한 것, 아직 모르는 것을 섞지 않는다.

VERIFIED · 직접 확인
  • 사이트 repo와 Talkak repo는 별개였고, Talkak 소스는 수정하지 않았다.
  • Dalkkak MCP가 pending 배포 제안을 만들고 Talkak Approvals가 표시했다.
  • 명시적 승인 후 클릭했지만 access token 오류로 실행 전 실패했다.
  • proposal과 승인 클릭은 배포 성공 증거가 아니며 PaaS 상태가 생성되지 않았다.
INFERENCE · 설계 추론
  • 강한 구조에서는 Runner만 외부 변경 credential을 소유해야 한다.
  • 승인은 exact payload·target·artifact digest와 결합돼야 한다.
  • 모든 CLI·HTTP·MCP·worker 경로는 하나의 정책·권한 경계로 수렴해야 한다.
UNKNOWN · 아직 미검증
  • Talkak 승인 내부에 어떤 lifecycle Hook 구현이 있는가.
  • 모든 직접 배포 credential과 우회 경로가 실제로 제거됐는가.
  • 승인과 artifact byte가 hash로 결합되고 replay가 완전히 차단되는가.
  • macOS와 Windows에서 같은 end-to-end 강제가 성립하는가.
모델에게 ‘하지 마’라고 말하는 것은 규칙이다. 모델에게 그 권한을 주지 않고, 승인된 Runner만 그 권한을 갖게 하는 것이 강제다.