facts: Node.js 기반 코어와 모델 독립적 인터페이스

Node.js와 TypeScript로 작성된 헤드리스 코어가 모든 요청을 처리하며, HTTP 인터페이스에는 Fastify를 사용한다. 영속 상태 저장을 위해 Postgres를 도입해 사용자 데이터, 세션 기록, 큐, 메모리를 관리한다. 외부 인터페이스로는 Vite로 빌드하고 Lit으로 렌더링하는 웹 UI와 Bolt 기반의 Slack 플러그인을 지원한다.

Pi, OpenCode, Codex, Claude Code 등 서로 다른 모델을 동일한 코어에 연결할 수 있는 구조다. 세션 저장소, 샌드박스, 메모리 기능을 인터페이스 뒤에 배치해 특정 모델 공급자에 종속되지 않도록 설계했다. 프로덕션 환경에서는 하나의 배선 파일을 통해 구현체를 교체하는 방식으로 운영한다.

배포는 운영자가 보유한 Fly.io 또는 AWS 계정에서 직접 수행한다. qm CLI를 통해 배포 디렉터리를 검증하며, 다음과 같은 명령어로 초기화할 수 있다.

bash
npm exec --yes --package=@yc-software/qm@latest -- qm init . --org --target

npm install

how-it-works: 범위(Scope) 기반 격리와 보안 정책 제어

개인과 공유 범위(scope)를 기본 단위로 설계해 작업 공간을 격리한다. 직원 개별 작업 공간은 독립적으로 운영되며, Slack 채널이나 그룹 메시지 같은 공유 범위에서는 여러 사용자가 동일한 에이전트와 협업한다. 각 범위는 별도의 메모리, 파일, 키체인, 권한, 예약 작업(cron), 웹 앱, 영구 샌드박스를 보유한다. 특히 `execute` 도구는 해당 범위에 할당된 영구 컴퓨터인 샌드박스 내에서만 명령을 실행하며, 설치된 도구는 세션이 종료되어도 유지된다.

보안 태세는 세 가지 모드로 구분해 제어한다. 'Strict' 모드는 효과가 없는 두 가지 턴 종료 작업을 제외한 모든 도구 호출을 사람의 승인 전까지 중단한다. 'Auto' 모드는 기본 설정으로, 외부 데이터와 도구 결과가 모델에 전달되기 전 분류기가 이를 검사하며 필요시 자체 검사 프록시를 지정할 수 있다. 'Dangerous' 모드는 콘텐츠 검사나 일시 중단 없이 작동한다.

모든 보안 모드에는 사전 선언된 명령 정책이 강제 적용된다. 재귀 삭제나 파괴적 SQL 작업과 같은 위험 명령은 승인 규칙과 상관없이 시스템 수준에서 거부된다. 에이전트는 협업하는 사용자의 자격 증명과 권한을 대행하며, 모든 수행 내역은 감사 기록에 남는다.

implementation-impact: 배포 저장소 분리와 프라이빗 미러 전략

조직별 구성, 사용자 정의 도구, 샌드박스 이미지 등 인프라 설정은 코어와 분리된 `deploy/layers/` 디렉터리에 보관한다. 이는 코어를 업스트림(upstream)과 바이트 단위로 동일하게 유지해 병합 규모를 줄이기 위한 전략이다. `update-qm`을 통해 업스트림 변경 사항을 병합하고, 조직 독립적인 수정 사항은 `upstream-pr`을 통해 전송한다. 이때 diff와 커밋 메시지에서 조직 식별자를 검사해 내부 정보 유출을 방지한다.

GitHub의 포크(Fork) 기능 대신 일반 복제(Mirror) 저장소를 권장한다. 공개 저장소의 포크는 비공개 전환이 불가능하며, 포크에 푸시한 커밋이 공개 측에서 SHA 값으로 조회될 수 있는 보안 취약점이 있기 때문이다. 일반 복제 저장소를 사용하면 이러한 객체 네트워크 공유 문제를 피할 수 있으나, 조직 계정에서 업스트림 CI 워크플로가 실행되므로 비밀 정보 제공이나 워크플로 비활성화 설정이 필요하다.

실무자는 에이전트의 권한 범위를 설계할 때 개인과 공유 scope의 경계를 명확히 구분하고, 특히 파괴적 SQL 방지 정책이 적용된 상태에서 샌드박스의 영구 저장소 유지 비용과 AWS/Fly.io의 인프라 운영 비용을 대조해 도입 규모를 결정해야 한다.