yc-software/qm · ★ 11,581 · 포크 739 · 이슈 86 · 언어 TypeScript · MIT · 2026-07-29 생성 → 며칠 만에 ★7k · src/ 약 7.5만 줄 / 47개 서브시스템 · Node 24 · 홈페이지 qm.ycombinator.com · TrendShift Daily #1)
qm은 "한 회사 안에서 모든 직원이 각자 격리된 작업공간으로 쓰면서 슬랙·웹에서 함께 협업하는, 모델·하네스를 자유롭게 갈아끼울 수 있는 셀프호스팅 조직용 AI 에이전트 코어"다. 개인 비서형 에이전트(ChatGPT, Claude 앱)와 다르게, qm의 기본 단위는 "사람 한 명"이 아니라 범위(scope)다. 나의 개인 scope, 우리 팀 채널 scope, 특정 프로젝트 scope, 회사 전체(org) scope — 각 scope가 자기만의 기억과 파일과 컴퓨터를 갖는다.
보통의 AI 비서는 모두가 공유하는 안내데스크 직원 한 명 같다. 편하지만 내 메모와 옆자리 동료의 메모가 뒤섞이고, 누가 무슨 권한으로 뭘 했는지 흐릿하다. qm은 정반대다 — 입사하면 각자에게 잠긴 책상, 개인 사물함(keychain), 전용 작업용 컴퓨터(durable 샌드박스)를 준다. 내 컴퓨터에 설치한 도구는 내 것으로 남고, 내 자격증명으로 로그인한 서비스는 나만 쓴다.
그러면서도 회의실(슬랙 채널·프로젝트)에 들어가면 같은 AI 동료가 그 방의 공유 기억을 갖고 함께 일한다. 관리자는 어떤 모델·하네스를 쓸지, 보안 수위를 어디까지 열지를 회사 차원에서 정하고, 각 scope는 그 기준을 더 조일 수만 있다(느슨하게는 못 한다). "개인화"와 "협업"과 "거버넌스"를 동시에 잡으려는 설계다.
사용자가 슬랙에서 한 줄 던졌을 때 qm 안에서 벌어지는 일을 압축하면 이렇다:
타이밍이 절묘하다. 2026년 중반은 "조직용 에이전트(agent for work)"가 격전지가 된 시점이다. Anthropic이 Cowork를 내놨고, 같은 트렌딩 목록에 different-ai/openwork("오픈소스 Claude Cowork 대안")가 나란히 올라와 있다. 이 흐름에서 qm의 각도는 뚜렷하다 — "개인용"이 아니라 처음부터 "멀티플레이어". 게다가 만든 곳이 Y Combinator(홈페이지 qm.ycombinator.com)라, 공개 며칠 만에 신뢰와 관심이 폭발했다.
둘째 이유는 벤더 중립이다. 대부분의 조직용 에이전트 제품은 특정 모델 회사에 묶인다(OpenAI면 OpenAI, Anthropic이면 Anthropic). qm은 하네스와 모델을 배포 설정 한 줄로 갈아끼운다. 오늘 Claude Code로 굴리다 내일 Codex로, 팀별로 다른 모델을 승인할 수도 있다. "AI 회사에 종속되지 않는 사내 에이전트 인프라"라는 포지션은 규제·비용·리스크를 신경 쓰는 조직에 특히 매력적이다.
사내 메신저를 자체 서버에 까는 것과 같은 결정. 슬랙(SaaS)을 쓰면 편하지만 데이터·정책·요금이 남의 손에 있다. 어떤 회사는 그래서 Mattermost를 자기 서버에 깐다. qm은 에이전트 판의 그 선택지다 — 우리 클라우드 계정(Fly.io나 AWS)에 우리가 깔고, 자격증명도 감사 로그도 샌드박스도 우리 안에 둔다. 대신 "클릭 한 번 가입"의 편함은 포기한다.
| 항목 | ChatGPT Team / Enterprise | Claude Cowork | 보통의 슬랙 봇 | 이 레포(qm) |
|---|---|---|---|---|
| 기본 단위 | 개인 비서 공유 | 개인 데스크톱 | 봇 1개(무상태) | scope별 격리 + 공유 |
| 호스팅 | 벤더 SaaS | 벤더 호스팅 | 대개 SaaS | 내 클라우드(Fly/AWS) |
| 모델·하네스 | OpenAI 고정 | Anthropic 고정 | 제각각 | Pi·Codex·Claude·OpenCode 교체 |
| 메모리·파일 | 계정 단위 | 로컬 단위 | 보통 없음 | scope별 durable 샌드박스 |
| 거버넌스 | 관리자 콘솔 | 제한적 | 직접 구현 | 3단 보안 posture + 명령 정책 + 감사 |
| 협업 표면 | 웹 앱 | 데스크톱 | 슬랙 | 슬랙 + 웹(동일 신원) |
셋째 이유는 엔지니어링의 진지함이다. README와 AGENTS.md를 읽으면 이게 데모가 아니라 운영을 전제로 짜였음이 드러난다: 코어는 blue-green·멀티 인스턴스로 돌아가고, "프로세스 메모리에 상태를 두지 말라(durable by default)"를 대원칙으로 못박았으며, 에이전트는 사람의 자격증명·권한으로 행동하되 모든 것이 감사(audit)된다. 보안 수위를 셋(Strict/Auto/Dangerous)으로 나누고, 어떤 수위에서도 "재귀 삭제·파괴적 SQL 같은 것은 하드 차단"하는 명령 정책은 절대 못 끄게 했다. 에이전트 인프라를 실제로 회사에 들이려는 사람이 볼 만한 설계다.
분석 시점 기준 이 레포는 공개 며칠 된 v0.1.0이었다(현재 v0.1.4, ★1.1만+). 별점은 7천을 넘겼지만 상당 부분 "YC + 시의성"의 힘이고, 오픈 이슈 86개가 말해주듯 API와 내부 구조는 아직 빠르게 흔들린다. 지금 프로덕션에 얹는 건 얼리어답터의 영역이다.
운영 부담도 실재한다. 이건 "가입하면 끝"인 SaaS가 아니라 내 클라우드에 직접 깔고 굴리는 인프라다 — Postgres, 샌드박스 이미지 빌드, 시크릿 관리, 슬랙 앱 매니페스트까지 챙겨야 한다. 또 하나 특이점: 기여를 코드가 아니라 "사람이 쓴 글"로만 받는다(adrs/에 원하는 변경을 서술 → 정렬되면 메인테이너가 구현). 커뮤니티 PR로 코드가 쌓이는 보통의 오픈소스와는 결이 다르니, "함께 만든다"를 기대하고 오면 어긋난다. 그리고 에이전트가 사용자 본인으로서 행동하는 모델은 강력하지만, Dangerous posture로 잘못 열면 그만큼 위험하다.
스택 선택이 인상적으로 절제돼 있다. 코어는 빌드 스텝 없이 TypeScript를 Node가 직접 실행한다(node src/index.ts). 트랜스파일 파이프라인이 없어 배포·디버깅이 단순해지는 대신, Node 24라는 비교적 최신 런타임을 요구한다. 무거운 프레임워크 대신 Fastify·Postgres·pg-boss 같은 검증된 조각들을 얇게 엮었다.
src/index.ts — 진입점. loadConfig() → buildApp() → createServer() 순으로 코어를 조립하고 슬랙 플러그인을 (있으면) 붙여 띄운다.node --test — 외부 테스트 러너 없이 표준 테스트 러너 사용. CI가 샤딩으로 병렬 실행.이 레포의 정체성. src/harness/가 서로 다른 코딩 에이전트를 하나의 Harness 인터페이스 뒤로 통일한다.
| 하네스 | 패키지 | 비고 |
|---|---|---|
pi (기본값) | @earendil-works/pi-coding-agent | HARNESS=pi가 디폴트. qm이 직접 관리하는 보안 빌드 tgz를 고정 참조 |
claude | @anthropic-ai/claude-agent-sdk | Claude Code 계열 루프 |
codex | @openai/codex | OpenAI Codex. app-server 모드 어댑터(codex-app-server.ts) 포함 |
opencode | @opencode-ai/sdk · plugin | OpenCode. 자체 플러그인 훅 |
mock | — | 테스트용 결정적 하네스 |
harness-router.ts의 resolveRuntimeChoice()가 "org이 승인한 하네스 목록 → scope별 선택 → 요청별 오버라이드"를 순서대로 해석해 최종 (하네스, 모델) 조합을 정한다. context-compaction.ts(긴 대화 압축)와 replay.ts·tape-fold.ts(세션을 "테이프"로 기록·재생)가 하네스 공통 기능으로 붙는다.
pg (node-postgres) — 유일한 durable 저장소. 세션 히스토리, 메모리, 감사 로그, 설정, 자격증명, 예산·레이트리밋까지 전부 Postgres 테이블(test/postgres-*.test.ts만 30개가 넘는다).pg-boss — Postgres 기반 작업 큐. 백그라운드 실행(src/runs/worker-main.ts)과 크론·와치를 이 큐로 돌린다.croner — 크론 표현식 파서. scope별 예약 작업(crons/watches)의 시간 계산.leader-lease)·어드바이저리 락(advisory-lock)까지 Postgres로 — 멀티 인스턴스에서 중복 실행을 막는 조율도 DB에 맡긴다.| 백엔드 | 구현 | 언제 |
|---|---|---|
| Local | local-sandbox.ts + docker-exec.ts | 개발·자체 호스트. 도커 컨테이너로 격리 |
| AWS | aws-sandbox.ts + aws-microvm-api.ts | 프로덕션. microVM(파이어크래커 계열) 격리 |
| Fly.io | sprites-sandbox.ts (@fly/sprites) | Fly 배포. 스프라이트 기반 격리 |
세 백엔드 모두 같은 샌드박스 인터페이스를 구현하고, sandbox-migrate.ts가 백엔드/이미지 교체 시 마이그레이션을 담당한다. 파일 입출력은 tar 스트림으로 오간다.
@slack/bolt + socket-mode + web-api. src/slack/가 32개 파일로 이 레포에서 가장 큰 표면. 코어가 인프로세스로 시작·감독한다.@vite 빌드 + Lit 렌더. 얇은 node:http 트랜스포트로 코어 HTTP API만 호출하는 커스텀 채팅 셸.cli/ (@yc-software/qm) — qm init으로 배포 디렉토리를 생성·검증·배포하는 별도 패키지.@modelcontextprotocol/sdk로 MCP 연동.qm의 아키텍처를 한 문장으로 요약하면 "작고 고정된 코어 + 그 위에 꽂는 교체 가능한 조각들"이다. 모든 턴은 중앙 코어를 통과하고, 코어는 여러 모델·하네스로 응답을 만든다. 사용자 데이터·세션·durable 상태는 Postgres에 있고, 에이전트가 실제로 명령을 실행하는 곳은 scope 전용 샌드박스다.
주목할 설계 선택: 에이전트가 쓰는 도구는 작고 고정돼 있고, 그중 하나가 execute(소스에선 exec)다. 파일 읽기/쓰기, 웹 검색, 메일 처리 같은 무한히 많은 능력은 도구를 늘려서가 아니라 execute가 scope 샌드박스 안에서 명령을 돌려 얻는다. 그 샌드박스는 durable이라, 한 번 설치한 도구는 다음 턴에도 그대로 있다. "에이전트에게 컴퓨터 한 대를 주는" 발상이다.
이 코드베이스에서 반복되는 패턴: 모든 기반요소(substrate)가 인터페이스 뒤에 있다. 하네스, 세션 저장소, 샌드박스, 메모리 — 전부 추상 인터페이스로 선언되고, 프로덕션 구현은 src/wiring.ts 한 파일에서 갈아끼운다. 테스트는 mock 구현을, 프로덕션은 Postgres·AWS 구현을 wiring에서 선택할 뿐, 코어 로직은 어느 구현인지 모른다. "무엇을 쓸지"와 "어떻게 동작할지"를 분리한 의존성 주입의 교과서적 적용이다.
또 하나의 큰 결정: 코어 트리는 회사가 누구든 바이트 단위로 동일하게 유지하고, 조직 고유의 것(org 설정, 커스텀 도구·스킬, 샌드박스 이미지, 인프라)은 전부 deploy/layers/<org>/ 한 디렉토리에 가둔다. 회사는 qm을 private fork(깃허브 Fork 버튼이 아니라 순수 clone)로 운영하고, update-qm·upstream-pr 두 스킬이 상류와의 경계를 양방향으로 지킨다. 코어가 상류와 동일하게 유지되니 업스트림 병합이 항상 작게 끝난다.
qm의 보안 모델은 로컬 코딩 에이전트(OpenCode·Codex·Claude Code)의 그것을 따른다: 에이전트는 자기가 일해주는 사람의 자격증명·권한으로 행동하고, 하는 모든 일이 감사된다. org은 하나의 보안 posture를 고르고, 하위 scope는 그걸 더 조이기만 할 수 있다.
| posture | 동작 | 성격 |
|---|---|---|
| Strict | 모든 하네스 도구 호출이 사람 승인에서 멈춘다(효과 없는 턴 종료 2개만 예외) | 가장 안전 · 가장 느림 |
| Auto (기본) | 출처 라벨이 붙은 외부 데이터·도구 결과를 분류기가 먼저 검열한 뒤 모델에 전달(자체 검열 프록시로 교체 가능) | 균형 · 디폴트 |
| Dangerous | 내용 검열 없음, 도구 호출 사이 멈춤 없음 | 가장 빠름 · 가장 위험 |
세 posture와 별개로, 미리 선언된 명령 정책(승인 규칙 + 재귀 삭제·파괴적 SQL 같은 것에 대한 하드 차단)이 모든 posture에서 항상 적용된다 — Dangerous 포함. "속도를 위해 검열을 끄더라도, 돌이킬 수 없는 파괴는 원천 차단"이라는 이중 안전망이다. 위협 모델·운영자 가정·알려진 한계는 SECURITY.md에 문서화돼 있다.
AGENTS.md가 대원칙으로 못박은 규율. 코어는 blue-green·멀티 인스턴스로 돌기 때문에, 프로세스 메모리의 Map이나 링버퍼는 인스턴스마다 다르고 배포 때마다 날아간다. 그래서 나중에 읽어야 하는 것(감사·로그·큐·해석된 설정)은 무조건 Postgres에 둔다. RAM은 durable 저장소 앞의 캐시이거나, 다시 만들어낼 수 있는 진짜 일회성 상태일 때만 허용된다. "에이전트가 실수로 회사 상태를 프로세스에 숨기는" 흔한 사고를 아키텍처 차원에서 막은 것.
AGENTS.md의 코딩 규율이 독특하다: 레포에 주석을 절대 남기지 않는다(설명 주석·TODO·타입 억제 지시문·주석처리된 코드 전부 금지). 의도는 이름과 구조와 테스트로 표현하고, 이유는 커밋 메시지·PR에 쓴다. "모든 경로가 지나는 층에서 고쳐라", "보고된 한 곳만이 아니라 같은 패턴 전부를 고쳐라", "머지 전엔 반드시 새 컨텍스트의 리뷰어가 변경을 깨보라(자기 리뷰 금지)" 같은 규율과 함께, 이 레포 자체가 "에이전트가 함께 일하기 좋은 코드베이스를 어떻게 만드는가"의 사례집이다.
구조가 곧 아키텍처다. src/의 47개 서브디렉토리가 도메인별로 또렷이 갈려 있고(신원·정책·샌드박스·하네스·메모리·크론…), 표면은 plugins/로 물리적으로 떨어져 코어를 import하지 못한다. 회사 고유의 모든 것은 deploy/layers/<org>/ 하나로 격리된다. "코어는 공용, 커스터마이징은 한 폴더"라는 원칙이 디렉토리 배치에 그대로 박혀 있어, 이 트리를 읽는 것만으로 시스템의 경계가 보인다.
가장 큰 서브시스템이 화려한 AI 로직이 아니라 HTTP API 표면과 슬랙 연동이라는 점에 주목. 이 레포의 무게중심은 "똑똑한 프롬프트"가 아니라 여러 사람·여러 표면·여러 하네스를 안전하게 엮는 통합·거버넌스에 있다. 에이전트를 조직에 들이는 진짜 어려움이 어디인지를 코드 분량이 정직하게 보여준다.
개인 비서 데모 수백 개와 갈라지는 지점이 여기다. scope라는 소유권 경계 하나로 격리(개인)와 협업(방)과 거버넌스(org)를 동시에 잡는 모델, 상위 정책을 하위가 "조이기만" 할 수 있는 상속 규칙, 에이전트가 사용자로서 행동하되 전부 감사되는 신뢰 모델 — 멀티테넌트 SaaS를 설계해 본 사람에게도 "AI 에이전트판 멀티테넌시"는 새로운 문제다. src/resolution/(설정 scope 해석)과 src/acl/·src/credentials/가 그 답안이다.
"모델에 종속되지 않는다"를 슬로건이 아니라 구조로 구현한 표본. 하네스·세션저장소·샌드박스·메모리를 전부 인터페이스로 선언하고 src/wiring.ts 한 곳에서 구현을 주입한다. harness-router.ts가 org 승인 목록 → scope 선택 → 요청 오버라이드를 해석하는 방식은, 다양한 백엔드를 런타임에 고르는 어떤 시스템에도 이식 가능한 뼈대다. 여기에 mock 구현까지 나란히 두어 테스트가 실물 없이 돈다.
blue-green·멀티 인스턴스에서 "상태를 프로세스에 두지 말라"를 규율로 삼고, 세션·메모리·큐·감사·심지어 리더 선출과 락까지 Postgres로 내린다. pg-boss 큐로 백그라운드 작업을 돌리고, 어드바이저리 락으로 중복 실행을 막는 실제 코드(test/leader-lease.test.ts, advisory-lock.test.ts)를 읽으면, "무상태 서비스 여러 대 + 하나의 durable 저장소"라는 정석을 에이전트 문맥에서 어떻게 지키는지가 보인다.
도구를 무한정 늘리는 대신 작고 고정된 도구 집합 + execute로 능력을 확장한다는 발상. 그 execute가 도는 scope 샌드박스는 durable이라 설치한 도구가 남는다. 같은 인터페이스를 local(docker)·AWS(microVM)·Fly(sprites) 셋으로 구현한 src/sandbox/는, 격리 기술(컨테이너 vs 마이크로VM)의 트레이드오프를 한 인터페이스 아래에서 비교하며 공부하기 좋은 교재다.
회사별 커스터마이징을 deploy/layers/<org>/ 하나로 가두고, 코어는 상류와 바이트 동일하게 유지하는 private fork 전략. update-qm(상류→포크 병합)·upstream-pr(조직 정보 유출 없이 포크→상류 기여) 두 스킬과, 플러그인이 코어를 import하지 못하게 한 chassis 규칙까지 — "오픈소스를 사내에서 divergence 없이 오래 운영하는" 소프트웨어 공급망 규율을 통째로 배울 수 있다.
| 구성요소 | 요구 | 비고 |
|---|---|---|
| 런타임 | Node ≥ 24.15 · npm ≥ 11.10 | 빌드 스텝 없이 TS 직접 실행. .node-version=24.18 |
| DB | Postgres | 세션·메모리·큐·감사·설정·자격증명 전부 여기 |
| 모델 키 | ANTHROPIC / OPENAI / OPENROUTER 중 | 선택한 하네스가 쓰는 provider 키 |
| 샌드박스 | Docker(로컬) / AWS microVM / Fly sprites | 배포 타깃에 따라 하나 |
| 배포 타깃 | Fly.io 또는 AWS | 운영자 본인 클라우드 계정에서 실행 |
| 슬랙(선택) | Bot Token + App Token | xoxb-… / xapp-…. 웹만 써도 됨 |
| 시크릿 | CORE_SIGNING · CAPABILITY · PORTAL_IDENTITY · CONNECTOR · SKILL_SIGNING | 서명·capability용 비밀들(.env.example에 목록) |
# qm init이 배포 디렉토리를 게시된 패키지에서 통째로 만들어 준다
npm exec --yes --package=@yc-software/qm@latest -- \
qm init . --org acme --target fly # 또는 --target aws
npm install
# → 배포용 스킬을 에이전트에게 넘기면 인프라·웹 로그인·커넥터·
# (선택)슬랙·배포·라이브 점검까지 안내대로 진행된다
기본 하네스는 HARNESS=pi, 기본 보안은 HARNESS_SECURITY_POSTURE=auto. 예산 가드레일도 env에 있다 — 창(window)당 요금 상한(BUDGET_USD_PER_WINDOW), org 단위 상한, 레이트리밋까지 기본값이 잡혀 있어 폭주 비용을 구조적으로 막는다.
git clone --depth 1 후 README의 mermaid 아키텍처를 손으로 다시 그려 보고, src/ 47개 서브디렉토리 이름만 훑으며 "무엇이 도메인 경계인지"를 지도로 만들어 보자. AGENTS.md의 코딩 규율(주석 0줄, 모든 경로가 지나는 층에서 고치기)이 왜 나왔을지 추측해 보는 게 관전 포인트.
.env.example을 복사해 Postgres 접속과 provider 키를 채우고, npm run dev-instance로 로컬 인스턴스를 올려 보자(HARNESS=pi, 로컬 도커 샌드박스). 웹 UI로 접속해 한 턴을 돌리고, npm run dev-instance:doctor로 무엇이 빠졌는지 진단 리포트를 읽는다.
HARNESS를 바꾸고 같은 요청을 던져 응답 차이를 관찰. 그다음 src/harness/harness-router.ts의 resolveRuntimeChoice()를 읽으며 "org 승인 목록 → scope 선택 → 요청 오버라이드"의 우선순위가 코드로 어떻게 표현됐는지 따라가 보자. src/harness/harness.ts의 HarnessTurnInput 인터페이스가 모든 하네스가 지켜야 하는 계약임을 확인.
개인 DM에서 "내 이름은 X"를 기억시키고, 공유 채널에서 같은 걸 물어 보자. scope가 다르면 기억이 새지 않아야 한다. src/memory/의 notebook 문법과 src/resolution/의 설정 해석을 읽으며, "어디까지가 내 것이고 어디부터가 우리 것인지"를 코드가 어떻게 가르는지를 추적한다.
qm init으로 배포 디렉토리를 만들어 본인 Fly/AWS 계정에 올리고(테스트 org), 슬랙 앱을 연결한다. 그다음 posture를 strict → auto → dangerous로 바꿔 가며 같은 파괴적 명령(예: 재귀 삭제)이 어느 posture에서도 하드 차단되는지를 직접 확인해 보자. 명령 정책이 posture와 독립임을 몸으로 이해하는 게 이 과제의 목표 — 단, 반드시 격리된 테스트 환경에서.
| 주차 | 주제 | 구체적으로 |
|---|---|---|
| 1주 | 조직용 에이전트·멀티테넌시 | 테넌트 격리, RBAC, scope/상속 모델, 감사 로그 설계. qm의 resolution/·acl/·audit/를 표본으로 |
| 2주 | 에이전트 하네스 4종 | Claude Agent SDK · OpenAI Codex · OpenCode · Pi 각각의 루프/도구 모델. 공통 인터페이스로 추상화하는 법(harness/) |
| 3주 | durable 분산 시스템 | Postgres 중심 상태, pg-boss 작업 큐, blue-green 배포, 리더 선출/어드바이저리 락, 멱등성(idempotency) |
| 4주 | 샌드박스·격리 기술 | 도커 격리 vs Firecracker microVM vs Fly sprites. 파일 tar 전송, durable 볼륨, 이미지 마이그레이션 |
| 5주(선택) | 보안·거버넌스 | OIDC + PKCE(내장 auth), provenance 라벨링과 콘텐츠 검열, 명령 정책(allow/deny), capability 서명(jose) |
execute가 scope 샌드박스 안에서 명령을 돌려 얻는 설계. 도구 폭발을 막는다.resolveRuntimeChoice() — org이 승인한 하네스·모델 목록에서 scope 선택과 요청 오버라이드를 해석해 이번 턴의 (하네스, 모델)을 결정하는 로직.src/wiring.ts 한 파일에서 주입하는 의존성 주입 패턴.deploy/layers/<org>/에 가두는 전략. 코어는 상류와 바이트 동일.src/credentials/).harness/replay.ts·tape-fold.ts).README.md(개요·아키텍처) · AGENTS.md(코딩 규율·private fork) · SECURITY.md(위협 모델) · src/wiring.ts(substrate 주입) · src/harness/(하네스 계약) · docs/getting-started.md(배포 흐름)@earendil-works/pi-coding-agent)