diff --git a/.env.example b/.env.example index 1a5921b..49d9b08 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,21 @@ # SPDX-License-Identifier: Apache-2.0 CODESPACE_HTTP_HOST=127.0.0.1 CODESPACE_HTTP_PORT=8787 +# This file is a reference; CodeSpace does not load it automatically. # Optional static Bearer for HTTP experiments. Leave empty to disable. # Never commit a real token. The server never logs this value. CODESPACE_HTTP_TOKEN= # JSON workspace registry (see docs/workspaces.example.json). # CODESPACE_CONFIG= +# Absolute path to persistent patch and instruction records, outside managed roots. +# CODESPACE_OPERATIONS_DB=/absolute/path/to/data/operations.sqlite +# Required unless the patch helper is beside codespace-mcp. +# CODESPACE_PATCH_BIN=/absolute/path/to/codespace-patch +# Operator-selected command timeout (default 30 seconds). +# CODESPACE_PROCESS_TIMEOUT_SECS=300 +# Optional same-host worker; does not itself sandbox commands. +# CODESPACE_RUNNER=uds +# CODESPACE_RUNTIME_BIN=/absolute/path/to/codespace-codex-runtime +# Linux command isolation requires a successful helper probe. +# CODESPACE_LINUX_SANDBOX_BIN=/absolute/path/to/codespace-linux-sandbox diff --git a/README.ko.md b/README.ko.md index 28e0225..1490e83 100644 --- a/README.ko.md +++ b/README.ko.md @@ -2,68 +2,47 @@ [English](README.md) | [한국어](README.ko.md) -개인용 **실행 도구 MCP 서버**입니다. ChatGPT, Cursor 또는 다른 MCP -클라이언트가 무엇을 할지 판단합니다. 이 프로세스는 워크스페이스 파일을 -읽고, 고정된 Rust `codex-apply-patch` 엔진으로 Codex 형식 패치를 적용하며, -등록된 워크스페이스에서 관리형 명령을 실행합니다. - -서버는 [`rmcp`](https://github.com/modelcontextprotocol/rust-sdk)로 만든 -**Cargo 워크스페이스**입니다(stdio와 Streamable HTTP). TypeScript -게이트웨이와 폐기된 `native/patch-worker`는 없습니다. 게이트웨이는 JSON -stdin/stdout으로 Rust `codespace-patch` 헬퍼와 대화합니다. 그 헬퍼 -프로세스는 Codex를 **프로세스 내부에서** 호출합니다. `exec_command`는 -현재 워크스페이스를 cwd로 하는 **호스트** 프로세스 -(`tokio::process::Command`)를 띄웁니다. 격리된 Linux 디스패치가 목표 -러너 경계이며, 현재 exec 경로는 아닙니다. 선택적 `CODESPACE_RUNNER=uds`는 -CodeSpace JSON으로 `codespace-codex-runtime`과 대화합니다. 그것은 Linux -격리가 아닙니다. - -다음이 **아닙니다**. - -- CoS 또는 cokacremote의 포크 -- Codex 에이전트 래퍼 -- 내부에서 모델을 호출하는 호스트 -- 완료된 Linux 샌드박스 러너 - -내부 모델 호출은 없습니다. MCP 클라이언트가 판단을 소유하고, CodeSpace는 -실행 계약을 소유합니다. 경로 정책, 작업 멱등성, 패치 적용/롤백 보고, -프로세스 수명입니다. - -## 상태 - -실제 MCP 도구는 `workspace_info`, `read`, `find`, `apply_patch`, -`operation_status`, `exec_command`, `write_stdin`, `read_process`, -`terminate_process`, `work_open`, `steer_status`, `steer_claim_next`, -`steer_complete`, `work_finish`입니다. Codex V4A 적용은 -`codespace-patch` 헬퍼 안의 `crates/patch`입니다 -([docs/upstream-lock.md](docs/upstream-lock.md) 참고). -Codex 제품 런타임은 제외합니다. 프리미티브 재사용: -[docs/codex-reuse.md](docs/codex-reuse.md). -실행 전용(Responses API 없음): -[docs/execution-substrate.md](docs/execution-substrate.md). -지연된 사용자 의도는 MCP가 아니라 HTTP `/inbox`에서 편집합니다. - -현재와 목표 프로세스 배치: -[docs/architecture.md](docs/architecture.md). -설치, HTTP/stdio, 로그, 복구: -[docs/operations.md](docs/operations.md). -기능을 `main`에 직접 커밋하지 마세요. - -## 실행 - -```bash -cargo run -p codespace-server --bin codespace-mcp -# Streamable HTTP at /mcp (default 127.0.0.1:8787); user inbox at /inbox: -cargo run -p codespace-server --bin codespace-mcp -- --http -``` - -시험: `cargo test --workspace`와 -`cargo test --manifest-path crates/patch/Cargo.toml`와 -`cargo test --manifest-path crates/codex-runtime/Cargo.toml`. ChatGPT Custom -Connector 절차와 **검증하지 않은** 항목: -[docs/chatgpt-connector.md](docs/chatgpt-connector.md). -운영자 설치: [docs/operations.md](docs/operations.md). +CodeSpace는 외부 코딩 에이전트가 작업 공간의 파일을 읽고 수정하며 명령을 실행할 수 있게 하는 MCP(Model Context Protocol) 서버입니다. 에이전트가 작업을 계획하고 결과를 해석하면, CodeSpace는 접근 권한을 확인하고 작업을 실행하며 실행 상태를 관리합니다. 자체적으로 모델을 호출하거나 에이전트 루프를 실행하지 않습니다. + + + + +## 작업 공간을 등록하고 시작하기 + +[설치와 첫 연결](docs/ko/operations.md)에서 서버·패치 도우미 빌드, 프로젝트 디렉터리 등록, stdio 또는 Streamable HTTP 연결 순서를 확인하세요. 작업 공간을 등록하지 않고 서버만 시작하면 접근할 수 있는 프로젝트가 없습니다. + +이후 [Agent Loop 연동 가이드](docs/ko/agent-integration.md)에 따라 읽기 → 수정 → 실행 → 결과 확인 흐름을 구성하세요. 취소와 결과가 불확실한 경우의 처리도 설명합니다. 상세 문서는 [문서 안내](docs/ko/index.md)에서 찾을 수 있습니다. + +## 제공하는 도구 + +| 목적 | 도구 | +| --- | --- | +| 실행 환경과 파일 확인 | `workspace_info`, `find`, `read` | +| 패치 적용과 기록된 상태 조회 | `apply_patch`, `operation_status` | +| 프로세스 실행과 제어 | `exec_command`, `read_process`, `write_stdin`, `terminate_process` | +| 작업과 사용자 추가 지시 관리 | `work_open`, `steer_status`, `steer_claim_next`, `steer_complete`, `work_finish` | + +두 전송 방식에서 같은 도구를 사용할 수 있습니다. HTTP `/inbox` API는 사용자용 클라이언트가 지시 초안을 관리하는 JSON API입니다. 브라우저에서 사용하는 받은 편지함 화면은 제공하지 않습니다. + + + + +## 실행 방식과 현재 제약 + +기본 러너는 서버 호스트에서 실행합니다. 선택적으로 Unix 소켓을 사용하는 별도 프로세스에 실행을 맡길 수 있지만, 실행 위치는 같은 호스트입니다. Linux에서는 샌드박스 도우미의 사용 가능 검사가 성공하면 명령 격리와 네트워크 정책이 적용됩니다. UDS 사용과 샌드박스 활성화는 별개입니다. 자세한 조건은 [러너 격리](docs/ko/runner-isolation.md)를 참고하세요. + +패치, 터미널 세션, 파일 시스템 접근, Linux 샌드박스에는 특정 버전에 고정한 Codex 실행 라이브러리를 사용합니다. [Codex 재사용 범위](docs/ko/codex-reuse.md)에서 실제 연결된 구성 요소와 CodeSpace가 담당하는 책임을 설명합니다. + +에이전트를 연동할 때는 다음 제약을 반영해야 합니다. + +- 프로세스 결과에는 출력과 EOF가 있지만 종료 코드는 없습니다. EOF만으로 테스트 성공을 판단할 수 없습니다. +- 프로세스 출력의 보관 크기가 제한되어 있으며, 유실된 출력을 알리는 별도 필드는 없습니다. +- 명령이 실행 중이면 같은 작업 공간에서 다른 명령이나 패치를 실행할 수 없습니다. 개발 서버를 켜 둔 채 같은 작업 공간을 수정하는 흐름에는 제약이 있습니다. +- 서버를 재시작하면 프로세스 핸들이 사라집니다. 패치 작업 기록은 데이터베이스 경로를 설정한 경우에만 유지됩니다. +- 컨테이너 실행과 완전한 OAuth 서버는 구현되어 있지 않습니다. 실제 ChatGPT 계정 연결도 아직 검증되지 않았습니다. + + ## 라이선스 -Apache License 2.0. [LICENSE](LICENSE)와 [NOTICE](NOTICE)를 보세요. +Apache License 2.0을 따릅니다. [LICENSE](LICENSE)와 [의존성 출처 고지](NOTICE)를 확인하세요. diff --git a/README.md b/README.md index fc4d4a1..b1d904d 100644 --- a/README.md +++ b/README.md @@ -2,67 +2,43 @@ [English](README.md) | [한국어](README.ko.md) -Personal **execution-tools MCP server**. ChatGPT, Cursor, or another MCP -client decides what to do. This process reads workspace files, applies -Codex-format patches through a pinned Rust `codex-apply-patch` engine, -and runs managed commands in the registered workspace. - -The server is a **Cargo workspace** built with [`rmcp`](https://github.com/modelcontextprotocol/rust-sdk) -(stdio and Streamable HTTP). There is no TypeScript gateway and no legacy -`native/patch-worker`. The gateway talks to a Rust `codespace-patch` -helper over JSON stdin/stdout. That helper process calls Codex -**in-process**. `exec_command` currently spawns a **host** process -(`tokio::process::Command`) with the workspace as cwd. Isolated Linux -dispatch is the target runner boundary, not the current exec path. -Opt-in `CODESPACE_RUNNER=uds` talks CodeSpace JSON to -`codespace-codex-runtime`; that is not Linux isolation. - -This is **not**: - -- a fork of CoS or cokacremote -- a Codex agent wrapper -- a host that calls a model internally -- a completed Linux sandbox runner - -There are no internal model calls. The MCP client owns judgment; CodeSpace -owns execution contracts: path policy, operation idempotency, patch -apply/rollback reporting, and process lifetime. - -## Status - -Live MCP tools include `workspace_info`, `read`, `find`, `apply_patch`, -`operation_status`, `exec_command`, `write_stdin`, `read_process`, -`terminate_process`, `work_open`, `steer_status`, `steer_claim_next`, -`steer_complete`, and `work_finish`. Codex V4A apply is `crates/patch` -inside the `codespace-patch` helper -(see [docs/upstream-lock.md](docs/upstream-lock.md)). -Codex product runtime stays out; primitive reuse: -[docs/codex-reuse.md](docs/codex-reuse.md). -Execution-only (no Responses API): -[docs/execution-substrate.md](docs/execution-substrate.md). -Deferred user intent is edited on HTTP `/inbox` (not MCP). - -Current vs target process layout: -[docs/architecture.md](docs/architecture.md). -Install, HTTP/stdio, logs, and recovery: -[docs/operations.md](docs/operations.md). -Do not commit features directly to `main`. - -## Run - -```bash -cargo run -p codespace-server --bin codespace-mcp -# Streamable HTTP at /mcp (default 127.0.0.1:8787); user inbox at /inbox: -cargo run -p codespace-server --bin codespace-mcp -- --http -``` - -Tests: `cargo test --workspace` and -`cargo test --manifest-path crates/patch/Cargo.toml` and -`cargo test --manifest-path crates/codex-runtime/Cargo.toml`. ChatGPT Custom -Connector steps and what is **not** verified: -[docs/chatgpt-connector.md](docs/chatgpt-connector.md). -Operator install: [docs/operations.md](docs/operations.md). +CodeSpace is an MCP (Model Context Protocol) server. It gives an external coding agent a workspace it can inspect, edit, and run commands in through MCP. Your agent plans the work and interprets results; CodeSpace checks workspace permissions, performs operations, and keeps their execution state. It does not call a model or run an agent loop. + + + +## Start with a registered workspace + +Follow [installation and first connection](docs/operations.md) to build the server and patch helper, register a project directory, and connect over stdio or Streamable HTTP. Starting the binary without a workspace registry leaves it with no accessible projects. + +Then use the [Agent Loop integration guide](docs/agent-integration.md) for the read → patch → run → inspect cycle, including cancellation and uncertain results. The [documentation overview](docs/index.md) points to reference material. + +## Available tools + +| Purpose | Tools | +| --- | --- | +| Inspect the environment and files | `workspace_info`, `find`, `read` | +| Apply a patch and retrieve its recorded state | `apply_patch`, `operation_status` | +| Run and control a process | `exec_command`, `read_process`, `write_stdin`, `terminate_process` | +| Track a logical job and queued user instructions | `work_open`, `steer_status`, `steer_claim_next`, `steer_complete`, `work_finish` | + +Both transports expose the same tools. The HTTP `/inbox` API lets a user-facing client manage instruction drafts; it is a JSON API, not a browser inbox application. + + + +## Execution and current limits + +The default runner executes on the server host. An optional Unix-socket worker moves execution into a separate process on that same host. On Linux, a successful sandbox-helper probe enables command isolation and network enforcement. These are separate choices: UDS alone does not provide sandboxing. See [runner isolation](docs/runner-isolation.md). + +CodeSpace reuses pinned Codex execution libraries for patches, terminal sessions, filesystem operations, and Linux sandboxing. [Codex reuse](docs/codex-reuse.md) explains which components are connected and which responsibilities stay in CodeSpace. + +For agent integrations, account for these limits: + +- Process results expose output and EOF, but no exit code. EOF alone cannot establish that a test passed. +- Process output is bounded; dropped output has no explicit flag in the MCP result. +- A live command blocks another command or patch in the same workspace. A development server cannot remain running while that workspace is patched. +- Process handles do not survive server restart. Patch-operation records persist only when a database path is configured. +- Container dispatch and a full OAuth server are not implemented. A live ChatGPT account connection remains unverified. ## License -Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE). +Apache License 2.0. See [LICENSE](LICENSE) and [dependency attribution](NOTICE). diff --git a/deploy/README.md b/deploy/README.md index 307db99..a18926f 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,8 +1,13 @@ -# Deploy examples +# Container isolation fixture -See [docs/operations.md](../docs/operations.md) for install, env vars, -recovery, and what is unverified. +This fixture starts a non-root `sleep infinity` process. It does not install +CodeSpace, start an MCP server, or receive Runner commands. Registering a +`linux-container` environment does not make it an implemented backend. -- `Dockerfile` / `compose.yml`: unprivileged Linux runner. The only bind - mount is `${CODESPACE_WORKSPACE}` → `/workspace`. Do not add host home, - SSH agent, Docker socket, or gateway secrets. +`Dockerfile` / `compose.yml` mount only `${CODESPACE_WORKSPACE}` at `/workspace`. +Do not add host home directories, SSH agent or Docker sockets, or gateway secrets. + +For an operational MCP server and the optional Linux sandbox helper, follow +[installation and operations](../docs/operations.md). The distinction between +host execution, the UDS worker, and command isolation is explained in +[runner isolation](../docs/runner-isolation.md). diff --git a/docs-site/.vitepress/theme/PageTools.vue b/docs-site/.vitepress/theme/PageTools.vue index ef65f9e..37ce7be 100644 --- a/docs-site/.vitepress/theme/PageTools.vue +++ b/docs-site/.vitepress/theme/PageTools.vue @@ -16,7 +16,7 @@ async function copyPage() { if (!response.ok) throw new Error('Markdown is unavailable'); const text = await response.text(); await navigator.clipboard.writeText(text); - if (route.path === current) status.value = ko.value ? '원문을 복사했습니다.' : 'Original Markdown copied.'; + if (route.path === current) status.value = ko.value ? '이 페이지의 Markdown을 복사했습니다.' : 'This page’s Markdown was copied.'; } catch { if (route.path === current) status.value = ko.value ? '복사하지 못했습니다. 브라우저 권한을 확인하세요.' : 'Copy failed. Check browser permissions.'; } finally { busy.value = false; } diff --git a/docs-site/.vitepress/theme/index.ts b/docs-site/.vitepress/theme/index.ts index 96705a3..b65e4c5 100644 --- a/docs-site/.vitepress/theme/index.ts +++ b/docs-site/.vitepress/theme/index.ts @@ -25,9 +25,9 @@ export default { }, 'home-features-after': () => { const ko = lang.value.startsWith('ko'); - const labels = ko ? ['빌드', '검증', '산출물', '게시'] : ['Build', 'Verify', 'Artifact', 'Publish']; - return h('section', { class: 'deployment-path', 'aria-label': ko ? '배포 순서' : 'Deployment steps' }, [ - h('p', { class: 'deployment-caption' }, ko ? '한 번 검증한 산출물로 게시합니다.' : 'Publish the artifact you verified.'), + const labels = ko ? ['작업 공간 등록', 'MCP 연결', '읽기·수정·실행', '결과 확인'] : ['Register a workspace', 'Connect MCP', 'Read, edit, run', 'Inspect results']; + return h('section', { class: 'deployment-path', 'aria-label': ko ? 'CodeSpace 사용 순서' : 'CodeSpace workflow' }, [ + h('p', { class: 'deployment-caption' }, ko ? '에이전트가 작업을 판단하고, CodeSpace가 실행합니다.' : 'Your agent directs the work. CodeSpace executes it.'), h('ol', labels.map((text, index) => h('li', [h('span', { class: 'step-number', 'aria-hidden': 'true' }, String(index + 1).padStart(2, '0')), text]))), ]); }, diff --git a/docs-site/scripts/site.py b/docs-site/scripts/site.py index f5f5f6b..1140bf3 100644 --- a/docs-site/scripts/site.py +++ b/docs-site/scripts/site.py @@ -203,6 +203,25 @@ def output_markdown(route): return route.lstrip('/') + '.md' +def home_features(inventory, locale): + descriptions = { + 'en': [ + ('getting-started', 'Get started', 'Understand what CodeSpace provides, its limits, and how to connect your first workspace.'), + ('agent-integration', 'Connect an Agent Loop', 'Follow tool calls from reading and patching through execution, cancellation, and recovery.'), + ('operations', 'Operate and troubleshoot', 'Build the helpers, register workspaces, configure transports, and diagnose failures.'), + ], + 'ko': [ + ('getting-started', '시작하기', 'CodeSpace의 기능과 한계를 살펴보고 첫 작업 공간을 연결하는 방법을 알아봅니다.'), + ('agent-integration', 'Agent Loop 연동', '읽기와 패치부터 명령 실행, 취소, 복구까지 도구 호출 순서를 따라갑니다.'), + ('operations', '설치·운영·문제 해결', '도우미 빌드, 작업 공간 등록, 전송 방식 설정과 오류 확인 방법을 설명합니다.'), + ], + } + return [{'title': title, 'details': details, + 'link': next(page['route'] for page in inventory + if page['id'] == ident and page['locale'] == locale)} + for ident, title, details in descriptions[locale]] + + def prepare(root=ROOT): root = root.resolve() commit = git(root, 'rev-parse', 'HEAD') @@ -229,19 +248,12 @@ def prepare(root=ROOT): copy = source / 'public' / page['copy'] copy.parent.mkdir(parents=True, exist_ok=True) copy.write_bytes(raw) - group_rank = {group['id']: index for index, group in enumerate(groups)} - en_guides = [page for page in inventory if page['locale'] == 'en'] - en_guides.sort(key=lambda page: (group_rank[page['section']], page['order'])) - feature_sources = en_guides[:3] for locale, tagline, start in ( - ('en', 'The client decides. This process reads, patches, and runs.', 'Start here'), - ('ko', '클라이언트가 판단합니다. 이 프로세스는 읽고, 패치하고, 실행합니다.', '시작하기'), + ('en', 'Workspace tools for an external coding agent: inspect files, apply patches, and run commands.', 'Start here'), + ('ko', '외부 코딩 에이전트가 파일을 읽고, 패치를 적용하고, 명령을 실행하는 작업 공간 도구입니다.', '시작하기'), ): prefix = '/ko' if locale == 'ko' else '' - features = [] - for page in feature_sources: - mate = next(item for item in inventory if item['id'] == page['id'] and item['locale'] == locale) - features.append({'title': mate['title'], 'details': tagline, 'link': mate['route']}) + features = home_features(inventory, locale) data = { 'layout': 'home', 'docLocale': locale, 'hero': { diff --git a/docs-site/tests/site.test.mjs b/docs-site/tests/site.test.mjs new file mode 100644 index 0000000..529eebb --- /dev/null +++ b/docs-site/tests/site.test.mjs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import test from 'node:test'; +const root = fileURLToPath(new URL('../../', import.meta.url)); +function python(script) { + return execFileSync(process.env.DOCS_PYTHON || 'python3', ['-B', '-c', script], { cwd: root, encoding: 'utf8' }); +} +test('compatibility anchors count as targets, but fenced examples do not', () => { + assert.equal(python(` +import importlib.util +s=importlib.util.spec_from_file_location('docs', 'scripts/check_docs.py') +m=importlib.util.module_from_spec(s);s.loader.exec_module(m) +text='# New heading\\n\\n\\n~~~html\\n\\n# Example\\n~~~\\n' +assert m.headings(text)==['new-heading','old-heading','이전'] +print('ok')`).trim(), 'ok'); +}); +test('home cards have distinct purposes and locale-specific guide links', () => { + assert.equal(python(` +import importlib.util +s=importlib.util.spec_from_file_location('site', 'docs-site/scripts/site.py') +m=importlib.util.module_from_spec(s);s.loader.exec_module(m) +_, pages=m.pages(m.ROOT) +for locale in ['en','ko']: + cards=m.home_features(pages,locale) + prefix='/ko' if locale=='ko' else '' + assert [c['link'] for c in cards]==[prefix+'/guide/'+p for p in ['getting-started','agent-integration','operations']] + assert len({c['details'] for c in cards})==3 +print('ok')`).trim(), 'ok'); +}); +test('Korean body links stay Korean and explicit anchors survive generation', () => { + assert.equal(python(` +import importlib.util +s=importlib.util.spec_from_file_location('site', 'docs-site/scripts/site.py') +m=importlib.util.module_from_spec(s);s.loader.exec_module(m) +_, pages=m.pages(m.ROOT) +routes={p['source']:p['route'] for p in pages} +text='\\n[설치](operations.md)\\n' +actual=m.remap(text,'docs/ko/agent-integration.md',routes,m.ROOT,'a'*40) +assert actual=='\\n[설치](/ko/guide/operations)\\n' +print('ok')`).trim(), 'ok'); +}); diff --git a/docs/agent-integration.md b/docs/agent-integration.md new file mode 100644 index 0000000..0d5faca --- /dev/null +++ b/docs/agent-integration.md @@ -0,0 +1,165 @@ +# Connect an Agent Loop + +[English](agent-integration.md) | [한국어](ko/agent-integration.md) + +CodeSpace is the execution layer of your agent. The outer loop selects tools, writes patches, interprets results, and decides when a task is complete. Start with [a configured server and registered workspace](operations.md). + +## Connect and inspect capabilities + +Use an MCP client SDK to initialize stdio or Streamable HTTP, send the initialized notification, and discover tools with `tools/list`. The repository tests use MCP 2025-11-25 as the baseline. Do not send tool arguments as an ordinary HTTP body to `/mcp`; let the SDK manage MCP framing, session headers, and result decoding. + +Examples below are `tools/call` parameter objects, not complete JSON-RPC messages. Tool results may be exposed by your SDK as structured content or text containing JSON. Check both MCP errors and the tool's own result before continuing. + +```json +{ + "name": "workspace_info", + "arguments": { + "workspace_id": "demo" + } +} +``` + +Inspect `execution.files.*.available`, `execution.process.available`, `execution.isolation.command_sandbox`, and `execution.network`. The tool list says what exists; workspace information says what the registered environment permits and supports. Availability does not reserve the workspace. Read-only workspaces cannot run commands. + +## Read and patch a file + +Use a disposable project for this example. Create `hello.txt` with `hi` followed by a newline before connecting. First read it: + +```json +{ + "name": "read", + "arguments": { + "workspace_id": "demo", + "path": "hello.txt" + } +} +``` + +Keep the returned `version`. Paths are relative to the registered root. `find` accepts a path glob, not a content search query. `read` returns at most 1 MiB and `find` returns a bounded list; their `truncated` flag means you did not receive the whole result. There is no public range-read or pagination argument. + +Replace `VERSION_FROM_READ` below with the exact version returned by `read`. V4A is Codex’s text patch format, using markers such as `*** Begin Patch` and `*** Update File`. This is a complete V4A patch, with JSON newline escapes: + +```json +{ + "name": "apply_patch", + "arguments": { + "workspace_id": "demo", + "patch": "*** Begin Patch\n*** Update File: hello.txt\n@@\n-hi\n+hello\n*** End Patch\n", + "expected_versions": { + "hello.txt": "VERSION_FROM_READ" + }, + "operation_key": "hello-preview-1", + "check_only": true + } +} +``` + +A preview returns `status: "checked"` without writing. To apply, send the same patch and expected version with `check_only: false` and a **different** key, such as `hello-apply-1`. The preview and apply requests differ and cannot share an idempotency key. Read the file again after applying. See [patch states and recovery limits](behavior-differences.md). + +## Run and observe a command + +```json +{ + "name": "exec_command", + "arguments": { + "workspace_id": "demo", + "command": [ + "/bin/echo", + "agent-smoke" + ] + } +} +``` + +The command is an argument array. Shell quoting, pipes, and `&&` are not interpreted unless you explicitly launch a shell. The working directory is the workspace root; environment and timeout are operator-controlled. Add `"tty": true` to allocate a pseudo-terminal (PTY) when a program requires a terminal (fixed 24×80; no resize API). + +The response contains a server-issued `process_id` and `dispatch_status`. `confirmed` means dispatch was acknowledged, **not that the command succeeded**. Save the ID, then poll with a modest delay and pass the returned cursor into the next read: + +```json +{ + "name": "read_process", + "arguments": { + "process_id": "PROCESS_ID_FROM_EXEC", + "cursor": 0 + } +} +``` + +Each result has `chunk`, `cursor`, and `eof`. Output combines stdout/stderr without preserving their identity. EOF means output collection is complete; it is not a successful exit status. MCP currently exposes no exit code. The last 256 KiB are retained and older bytes can be dropped without an explicit loss flag. Do not claim a build or test passed solely from EOF or an incomplete log. If success cannot be established from a reliable task-specific result, report it as unverified. + + + +These example result bodies illustrate the command above. IDs are placeholders: use the values from your own responses. The examples omit the MCP envelope and show a call without coordination context. + +```json +{ + "process_id": "SERVER_ISSUED_PROCESS_ID", + "dispatch_status": "confirmed" +} +``` + +```json +{ + "process_id": "SERVER_ISSUED_PROCESS_ID", + "cursor": 12, + "chunk": "agent-smoke\n", + "eof": true +} +``` + +Interactive input and cancellation use the same handle: + +```json +{ + "name": "write_stdin", + "arguments": { + "process_id": "PROCESS_ID_FROM_EXEC", + "data": "input\n" + } +} +``` + +```json +{ + "name": "terminate_process", + "arguments": { + "process_id": "PROCESS_ID_FROM_EXEC" + } +} +``` + +A live command occupies the workspace. Wait for it to end or terminate it before applying a patch or starting another command. Reads and searches remain available. Long-lived development servers therefore require a workflow that stops them before edits. + +## Retry and recover deliberately + +| Situation | Agent action | +| --- | --- | +| Patch response lost | Query `operation_status` using the original key, or the operation ID if known | +| `VERSION_CONFLICT` | Read current content and produce a new patch; do not force the old one | +| `OPERATION_KEY_CONFLICT` | The key belongs to different arguments; inspect the earlier request | +| Patch `unknown` or `failed_partial` | Inspect affected files and report uncertainty before deciding on a new operation | +| Exec `dispatch_status: unknown` | A process may exist. Inspect or terminate the returned handle if reachable; do not blindly start another | +| `WORKSPACE_BUSY` | Wait for the owning task or cancel the process; avoid a tight retry loop | +| `TIMEOUT` | Treat execution as interrupted; inspect partial effects | +| Server/worker lost | Reconnect and inspect capabilities/files; old process handles are not recoverable | + +```json +{ + "name": "operation_status", + "arguments": { + "operation_key": "hello-apply-1" + } +} +``` + +Use exactly one lookup identifier. `operation_status` tracks patches, not commands. A request ID identifies a transport message; `operation_id` identifies a recorded patch; `process_id` identifies a managed process. None of these IDs grants authority. A recorded patch refusal after dispatch does not prove that disk contents are unchanged; inspect files when verification failed after application. + +## Handle user instructions and finish + +Optionally call `work_open` with `workspace_id` and a title. Pass its `work_id` on tools that accept it. At safe checkpoints, call `steer_status`, then `steer_claim_next`; process any claimed instruction and mark it `done` or `blocked` with `steer_complete`. Counts in `coordination` are hints, not the instruction body. + +Users create and queue drafts through HTTP `/inbox`; drafts are not delivered until queued. This API requires HTTP mode and has no built-in browser UI. Queued text never changes workspace permissions. + +Call `work_finish` with the work ID after draining instructions. If it returns `closed: false` and `reason: "pending_user_input"`, handle the remaining queue instead of declaring completion. This closes coordination state; it does not certify code correctness or replace test evidence. + +A useful integration acceptance test is: read → preview → apply → read back → run → collect output → cancel a long command → recover a patch by key. Add a failing command and an oversized log to confirm that your loop handles the current result limitations honestly. diff --git a/docs/architecture.md b/docs/architecture.md index 82b4863..cf79ef3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,310 +1,68 @@ + + # Architecture [English](architecture.md) | [한국어](ko/architecture.md) -CodeSpace is a personal **execution-tools MCP server**. An outer client -(ChatGPT, Cursor, or another MCP host) decides what to do. This process -never calls a model. It reads files, applies Codex V4A patches through a -pinned Rust engine, and runs managed commands in a registered workspace. - -## Identity - -| Is | Is not | -| --- | --- | -| Independent MCP server | Fork of CoS or cokacremote | -| Execution-only environment + contract | Codex agent / App Server wrapper | -| File read / patch / managed exec | Internal chat, Goal/Loop, multi-agent, Responses API | -| Gateway policy + (target) OS/container isolation | Kernel sandbox equivalent to Codex CLI | - -Borrowed ideas (not code dumps): - -- From cokacremote: headless server, Streamable HTTP, separating **request - lifetime** from **process lifetime**. -- From CoS: approved workspaces, per-hunk path resolve before the engine, - preflight, best-effort rollback, split tool surface. -- From Codex: original Rust `codex-apply-patch` parse / verify / apply, - and later a cohesive **execution subgraph** (hardening, PTY, UDS, - path, filesystem, Linux sandbox, network) isolated behind the - Runner. Codex is an implementation dependency, not the control plane - ([codex-reuse.md](codex-reuse.md)). - -Not taken: Electron, Chrome extension, ChatGPT DOM, agents spawn, Desktop, -plugin marketplace, TypeScript `apply-patch` port, `git apply --unsafe-paths`, -wrapping the standalone `apply_patch` binary as the security boundary -(that path uses sandbox `None` and follows symlinks by default), a -TypeScript MCP gateway, the retired `native/patch-worker` tree, or any -outbound model client. Execution-only invariant: -[execution-substrate.md](execution-substrate.md). +CodeSpace separates the agent's decisions from workspace execution. The MCP server owns authorization and operation records. A Runner performs already-authorized filesystem and process work. Codex libraries stay behind adapters. ## Current layout -MVP default is **one host process**: `codespace-mcp` plus in-process -`Runner`. `exec_command` is not dispatched into compose. -[`deploy/compose.yml`](../deploy/compose.yml) is an isolation **fixture** -only. Opt-in Unix-socket transport (`CODESPACE_RUNNER=uds`) talks -CodeSpace JSON to `codespace-codex-runtime`; that is not Linux -isolation and not the default. - ```text -CURRENT - -MCP Client - │ - ▼ -codespace-mcp (host gateway) - ├─ policy / store / coordination / operation persist - ├─ structured logging (stderr tracing) - │ - │ Runner execution DTO - │ (command/exec shape; no work_id / operation_id / coordination) - ▼ -RuntimeBackend - ├─ default: InProcessRunner - └─ opt-in: UdsRunner (CODESPACE_RUNNER=uds, 1:1) - │ length-prefixed CodeSpace JSON (protocol 1, Hello, request_id rrpc-…) - ▼ - codespace-codex-runtime (owned by gateway RuntimeProcess) - ├─ unique 0700 dir + runner.sock (bind only; no parent chmod) - ├─ codex-process-hardening (main first line, not a command sandbox) - ├─ disconnect / gateway shutdown → kill worker (host children die) - └─ one InProcessRunner - ├─ read / find / version (PathSandbox → codespace-fs) - ├─ apply_patch (one transaction) - │ expected versions → preflight → snapshot - │ → helper apply → verify → rollback - │ │ JSON stdin/stdout - │ ▼ - │ codespace-patch (host child) - │ └─ Codex Rust crate in-process - └─ exec / stdin / read / terminate - └─ host process (tokio::process::Command, - cwd = workspace root, env from runner-local defaults) - -deploy/compose.yml - └─ isolation fixture only; not connected to exec_command +External Agent Loop + → MCP server: registry, policy, patch records, work/instruction queue + → Runner + ├─ InProcessRunner (default) + └─ UdsRunner → codespace-codex-runtime → InProcessRunner + ├─ file operations → codespace-fs + ├─ patch transaction → codespace-patch + └─ process supervisor + ├─ pipes / codespace-pty + └─ Linux helper when available → sandbox / managed proxy ``` -```text -MCP JSON → domain params → gateway (policy/store) → Runner DTO → RuntimeBackend - │ - └─ rmcp / JsonSchema stay on MCP types, not on runner DTOs -``` - -The gateway owns **who may do what in which workspace**. Tokens, server -config, workspace registry, environments, and the operations database -live here. It maps MCP params onto runner DTOs and does **not** pass -`ExecCommandParams` into the runner. Tools still have no -`environment_id`. - -`crates/runner` owns the `Runner` trait, execution DTOs, -`InProcessRunner` (filesystem, one `apply_patch` transaction, host -process supervisor), `UdsRunner` (Unix-socket client), and -compose-fixture checks. Default backend is in-process. The worker -binary is isolated `crates/codex-runtime` / `codespace-codex-runtime`. - -`codespace-patch` is a product helper process, not the upstream -standalone `apply_patch` binary and not `native/patch-worker`. -Runner ↔ helper is JSON stdin/stdout. Codex itself runs in-process -**inside that helper**. - -Single instance is enough for MVP. SQLite stores **patch operations** -plus works/intents. Process handles and resource locks (workspace -exclusive write / shell occupancy) are in-memory. No message broker. - -Gateway unit tests may run on the macOS development host. A Linux -container is the **target** isolation OS, not the current exec boundary. -Registered `linux-container` environments fail closed (`UNAUTHORIZED`). - -## Target layout - -A later runner split stays Rust on both sides. Do not reintroduce a -TypeScript gateway or `native/patch-worker` just to call Codex. - -```text -TARGET - -MCP Client - │ - ▼ -Gateway - │ authorized RunnerRequest - │ (policy, operation_key replay, write lock, - │ dispatch, result persistence) - ▼ -Runner process boundary - │ - ├─ filesystem - ├─ patch transaction (one runner-side operation) - └─ process supervisor - │ - ▼ - isolated Linux workspace -``` +The UDS worker and default runner both execute on the same host. A Linux command sandbox can wrap either runner's spawn path. Neither path dispatches into the Compose container fixture. -Target **domain** (not live MCP fields): Environment (where), Workspace -(what), PermissionProfile (may), Operation (this RPC). Do not add -`environment_id` to tools. Operator config may register environments. -See [execution-substrate.md](execution-substrate.md). + + -Unix-socket **transport** (`UdsRunner`) exists behind the existing -`Runner` / `InProcessRunner` types as an opt-in. Host + UDS is the same -host; it does not claim Linux isolation. `LinuxContainer` stays -fail-closed (`UNAUTHORIZED`, no `operation_id`). It must **not** split -patch apply into multiple gateway-driven RPCs: +## Responsibilities and state -```text -Runner.apply_patch(request) - expected versions → path policy → preflight → snapshot - → Codex apply → after-version verify → rollback on failure -``` - -Gateway keeps authorization, `operation_key` replay, the write lock, -dispatch, and persistence. Default remains `InProcessRunner`. Opt-in -`CODESPACE_RUNNER=uds` uses a private Unix socket (unique 0700 leaf, -`runner.sock`), not the compose fixture. The gateway owns the worker -1:1 (`RuntimeProcess`, `kill_on_drop`). There is no reconnect. Live -sockets are connect-probed; leftovers are unlinked only on -`ConnectionRefused`. Runner `Replay` is same-connection only. - -Sandbox, PTY, and network isolation are **not** “reimplement Codex OS -engineering by default.” Prefer a cohesive execution subgraph isolated -behind the Runner, same pattern as `crates/patch` and -`crates/codex-runtime` / `codespace-codex-runtime`. This WP takes -`codex-process-hardening` and `codex-uds`. Codex types stay in the -adapter. Do not embed App Server or `codex-exec`. `codex-exec-server` -is a future measurement, not a current backend. - -## Protocol compatibility - -Core execution is **MCP 2025-11-25** over **stdio** and **Streamable HTTP -`/mcp`**. Required primitives are `initialize`, `tools/list`, and -`tools/call`. HTTP's spec floor is 2025-03-26 (Streamable HTTP exists); -CI pins 2025-11-25. **2026-07-28 is progressive enhancement only.** - -Core must not require MRTR, Tasks, subscriptions, `Mcp-Name` routing, or -0728 stateless lifecycle as application state. Auth stays optional Bearer -middleware; dispatch is `/mcp` → rmcp tools. `ProtocolVersion` and -`NegotiatedFeatures` live in `crates/server` only. - -2024-11-05 HTTP+SSE is not a goal. Full matrix: -[protocol-compatibility.md](protocol-compatibility.md). - -## MVP tools - -Execution tools: - -| Tool | Role | +| Component | Responsibility | | --- | --- | -| `workspace_info` | Identity, selector metadata, and effective execution contract (not a credential) | -| `read` | File contents + version | -| `find` | Relative-path search | -| `apply_patch` | Codex V4A only | -| `exec_command` | Start a managed **host** process | -| `write_stdin` | Write to a managed process | -| `read_process` | Cursor-based output | -| `terminate_process` | Kill a server-issued handle | -| `operation_status` | Recover by `operation_id` **or** `operation_key` | +| `server` | MCP transport, HTTP authentication/inbox, request validation and orchestration | +| `domain` | CodeSpace tool parameters, results, IDs, error and execution types | +| `policy` | Registered roots, environments, profiles, and network policy | +| `store` | SQLite patch operations, logical works, user instructions; in-memory occupancy | +| `runner` | Execution DTOs, file scope, patch transaction, process supervision, UDS client/server protocol | +| Isolated adapters | Codex patch, PTY, filesystem, worker hardening/socket, Linux sandbox mechanisms | -Coordination tools (ordinary `tools/call`, MCP 2025-11-25 first-class): +Patch operations and works/intents survive restart only with a configured SQLite file. Process handles and occupancy leases are memory-only. `operation_status` does not track exec requests. The transport request ID, patch operation ID, process ID, work ID, and instruction ID serve different purposes. -| Tool | Role | -| --- | --- | -| `work_open` | Mint a `work_id` for one logical job | -| `steer_status` | Counts only; no intent bodies | -| `steer_claim_next` | Atomically claim one queued item | -| `steer_complete` | Mark claimed intent done or blocked | -| `work_finish` | Close only if the queue is drained | - -Users edit drafts and reorder queued items on HTTP `/inbox`, not MCP. -Intent bodies are instructions, never capabilities. + -No internal model-calling tool exists. `git_apply_patch` is out of MVP. -Error codes and transport-vs-execution rules: [error-codes.md](error-codes.md). -Linux isolation fixture: [runner-isolation.md](runner-isolation.md). -Codex product vs primitive: [codex-reuse.md](codex-reuse.md). -Execution-only substrate: [execution-substrate.md](execution-substrate.md). +## Patch transaction -## IDs +The gateway authorizes the workspace, obtains the write lease, checks the operation key, dispatches one Runner patch request, and records its result. The Runner checks expected versions, performs preflight, snapshots affected files, invokes the patch helper, and verifies resulting disk hashes. -HTTP/JSON-RPC request id, `operation_id`, `operation_key`, `process_id`, -`work_id`, `intent_id`, and runner `request_id` (`rrpc-…`) are different -identifiers. A lost HTTP response is not an execution failure. A lost -UDS `apply_patch` response is recorded as `unknown`, not `rejected`. -Clients call `operation_status` instead of replaying a mutating tool. +If helper application fails, the Runner attempts per-file snapshot restoration. Post-apply verification errors currently propagate without entering that restoration branch. This is not an atomic filesystem transaction. See [patch behavior](behavior-differences.md) for client-visible consequences. -```text -Workspace (workspace_id) - └── Work (work_id) - ├── Operation (operation_id) - ├── Process (process_id) - └── User Intent Queue (intent_id) -``` +## Process lifetime -`workspace_id` and `work_id` are **selectors**, never proof of -authorization. Client arguments `approved: true` and `user_id` are -ignored. User-intent text does not raise the permission profile. +MCP request completion does not end a managed process. Clients continue with its `process_id`. Server restart loses those handles. In UDS mode the gateway owns the worker: internal disconnect/shutdown ends the worker and its children, with no reconnect. See [runner isolation](runner-isolation.md) for the distinct transport and isolation boundaries. -## Patch apply pipeline + + -Gateway keeps authorization, `operation_key` replay, the write lock, -and persistence. `InProcessRunner.apply_patch` runs the execution -transaction as a single call. Gateway does not split it into preflight / -snapshot / apply RPCs. +## Extension boundaries -```text -validate request - → auth + workspace policy - → operation_key replay / conflict - → workspace write lock - → Runner.apply_patch - expected_versions - → full preflight (no writes) - → save rollback snapshot - → original engine apply (`apply_patch_with_options`) - → verify disk hash == helper claimed after_version - → rollback on failure - → persist operation status (gateway fills operation_id) - → MCP response -``` +The core does not import Codex types directly. Adapters may depend on a broader Codex execution graph; this does not make the gateway a Codex agent. Operator configuration selects environments, while MCP clients select only registered workspaces. Container execution, remote runners, approval-resume tools, and a resource scheduler are not implemented. -`crates/patch` (inside `codespace-patch`) calls `parse_patch`, then -product policy, then `apply_patch_with_options` **in-process**. It does -not wrap the standalone `apply_patch` binary and does not reimplement -the parser. +Keep a patch transaction as one Runner call when adding transports. Keep permission decisions in the gateway rather than importing Codex user/session permissions as authority. [Execution contracts](execution-substrate.md) describe current invariants; [Codex reuse](codex-reuse.md) lists connected adapters. -`apply_patch` never silently falls back to `git apply`. Status values -are `applied` / `checked` / `rejected` / `failed_rolled_back` / -`failed_partial` / `unknown`. `checked` is a successful `check_only` -preview (no writes). `rejected` is an actual refusal. Transport -ambiguity (socket drop after dispatch) finishes `unknown` and must not -be stored as `rejected`. Success copy without after-version verification -is forbidden. - -Rollback must not use `git reset --hard` and must not overwrite a whole -directory tree as a substitute for per-file restore. - -## Repository layout - -```text -Cargo.toml workspace root -crates/server/ bin codespace-mcp: rmcp stdio + Streamable HTTP + /inbox -crates/domain/ workspace, capabilities, operation, errors (no rmcp) -crates/policy/ registry, PermissionProfile, Environment -crates/patch/ Codex adapter + codespace-patch helper (own workspace) -crates/codex-runtime/ isolated worker: hardening + UDS + InProcessRunner -crates/store/ SQLite operations, works, intents; in-memory resource locks -crates/runner/ Runner trait + execution DTOs, PathSandbox, patch transaction, host supervisor, UdsRunner, fixture checks -crates/file-system/ isolated Codex FS adapter (codespace-fs): no-follow I/O + bounded walk -third_party/codex/ git submodule, pinned revision (W06) -tests/{security,recovery,e2e}/ -docs/ including operations.md (W12), codex-reuse.md, - execution-substrate.md (W17) -deploy/ unprivileged Linux isolation fixture -``` + + -## Out of scope (initial) +## Tool and protocol references -Browser extension, ChatGPT DOM automation, Goal/Loop, multi-agent, -Desktop control, plugin marketplace, a full OAuth server, automatic -unified-diff conversion, forwarding the entire Codex App Server RPC, -internal model calls, TypeScript MCP SDK, `native/patch-worker`. +For callable tools and integration examples, use [Agent Loop integration](agent-integration.md). For revision negotiation and transport tests, use [protocol compatibility](protocol-compatibility.md). This avoids maintaining a second tool reference inside the architecture document. diff --git a/docs/behavior-differences.md b/docs/behavior-differences.md index b17d74c..f6d4976 100644 --- a/docs/behavior-differences.md +++ b/docs/behavior-differences.md @@ -1,62 +1,44 @@ -# Behavior differences + + +# Patch behavior and recovery [English](behavior-differences.md) | [한국어](ko/behavior-differences.md) -Product policy is **not** “whatever `codex-apply-patch` does by -default.” `crates/patch` calls the original parser and apply functions -in-process, then the gateway/runner refuse operations the crate might -accept. - -| Topic | Codex library / standalone default (0.154 candidate) | CodeSpace product | -| --- | --- | --- | -| Paths from the model | May accept host-absolute paths; resolve relative to process cwd | **Relative paths only**, resolved inside the registered workspace root | -| Symlinks | Apply options can follow / keep going | **Reject** symlink files and symlink-escape | -| Special files | Not a product gate | **Reject** devices, sockets, fifos | -| Add File | May interact with an existing path depending on hunks | **Reject** Add File if the destination already exists | -| Move destination | Engine may apply if the hunk says so | **Reject** if the move destination already exists | -| Newlines | Multiple modes exist; do not assume | **Preserve-newline mode preferred**; parity uses the same mode | -| `git apply` | Not the V4A engine; other products sometimes fall back | **No silent git-apply fallback** | -| Sandbox | Standalone `apply_patch` uses sandbox `None` | Patch crate is **not** the sandbox; gateway policy + PathSandbox today; Linux container is the **target** | -| Unified diff | Different tool elsewhere | Out of MVP (`git_apply_patch` later, never auto-convert) | -| Rollback | N/A in the crate | Snapshot restore of files; **no** `git reset --hard` | +CodeSpace accepts Codex V4A patches and applies additional workspace rules before calling the pinned library. A supported library option is not automatically an allowed service operation. ## Patch request contract -```json -{ - "workspace_id": "demo", - "patch": "*** Begin Patch\\n*** Update File: src/config.ts\\n...", - "expected_versions": { - "src/config.ts": "sha256:" - }, - "operation_key": "change-timeout-001", - "check_only": false -} -``` - -- `expected_versions` values are content versions from `read`, or - `"absent"` for a file that must not exist yet. Move validates source - and destination. -- `operation_key` is idempotency, not a capability token. -- `check_only: true` must leave every target file byte-identical and - returns `status: "checked"`. -- Successful apply returns `files` (path list) plus `changes` with - `path`, `before_version`, `after_version`, and `kind` - (`add` / `update` / `delete` / `move`). `applied` means the helper - claimed hash matches a fresh disk hash. - -Result `status`: `applied` | `checked` | `rejected` | -`failed_rolled_back` | `failed_partial` | `unknown`. - -## Write lock - -A `workspace-write` shell is a mutating occupant. While it is live, -other mutating patch/exec work on that workspace is blocked -(`WORKSPACE_BUSY`) or waits per a documented queue. The product does not -pretend a shell cannot delete workspace files. - -## Transport - -stdio and Streamable HTTP expose the **same** tool schemas. Optional -static Bearer is HTTP experiment only and is **not** assumed to satisfy -ChatGPT Custom Connectors until a live account check says so. +Use relative paths and versions returned by `read`. The version `absent` means the target must not exist. Include both source and destination versions when protecting a move. A complete [preview/apply example](agent-integration.md) shows how to change the operation key between those distinct requests. + +| Rule | CodeSpace behavior | +| --- | --- | +| Paths | Resolve within the registered root; reject absolute and escaping paths | +| Symlinks and special files | Reject symlink paths and devices, sockets, or FIFOs | +| Add or move destination exists | Refuse rather than overwrite the existing destination | +| Newlines | Request Codex `PreserveLineEndings`; selected parity cases are tested | +| Patch format | V4A only; no automatic unified-diff conversion or `git apply` fallback | +| Preview | `check_only: true` runs preflight without writing; it is not a reservation or proof that later apply will succeed | + +## Results and recovery limits + +| Status | Meaning and next action | +| --- | --- | +| `checked` | Preview passed; use a new operation key for actual application | +| `applied` | Post-apply disk hashes matched helper claims | +| `rejected` | Request was refused or an error was recorded; inspect the error and whether execution had already begun | +| `failed_rolled_back` | Helper application failed and snapshot restoration reported completion | +| `failed_partial` | Helper application failed and restoration was incomplete; inspect files | +| `unknown` | Final outcome is not known; inspect files and recorded state before retrying | + +The Runner snapshots affected files and restores them if the helper apply call fails. An error while verifying a successful helper response currently returns before restoration. The gateway can record that error as `rejected` even though files may have changed. Do not interpret every rejected result as proof of no writes. Crash recovery also does not automatically restore snapshots or replay work. No `git reset --hard` is used. + +A successful result includes affected `files` and `changes` with kind and available before/after hashes. Those hashes describe the observed files, not a repository commit or a successful build. + + + + +## Write lock and transport + +One live command blocks other patch/exec work in that workspace with `WORKSPACE_BUSY`. There is no waiting queue. Reads and searches remain possible. Patch operation keys support replay only for matching request fingerprints; choosing a new key after an uncertain response risks applying the change twice. + +stdio and Streamable HTTP expose the same tool schemas. Connection failure does not establish whether a mutation ran. See [error codes](error-codes.md) and [recovery rules](agent-integration.md). diff --git a/docs/chatgpt-connector.md b/docs/chatgpt-connector.md index f57a3f6..027fc8e 100644 --- a/docs/chatgpt-connector.md +++ b/docs/chatgpt-connector.md @@ -1,75 +1,42 @@ -# ChatGPT connector experiment + + +# ChatGPT connection status [English](chatgpt-connector.md) | [한국어](ko/chatgpt-connector.md) -CodeSpace is a Rust `rmcp` process. It speaks **stdio** and **Streamable HTTP** -with the same live tools (`workspace_info`, `read`, `find`, `apply_patch`, -`operation_status`, `exec_command`, `write_stdin`, `read_process`, -`terminate_process`, `work_open`, `steer_status`, `steer_claim_next`, -`steer_complete`, `work_finish`). User drafts are HTTP `/inbox`, not MCP. -`exec_command` is a host process today; the compose file is an isolation -fixture and is not a ChatGPT connection path. - -Core protocol baseline is **MCP 2025-11-25**. See -[protocol-compatibility.md](protocol-compatibility.md). - -## Local stdio (verified by cargo test) - -```bash -cargo run -p codespace-server --bin codespace-mcp -- --transport stdio -``` - -Cursor-style MCP config: - -```json -{ - "mcpServers": { - "codespace": { - "command": "cargo", - "args": ["run", "-p", "codespace-server", "--bin", "codespace-mcp", "--", "--transport", "stdio"], - "cwd": "/absolute/path/to/CodeSpace" - } - } -} -``` - -## Local Streamable HTTP (experiment) - -```bash -export CODESPACE_HTTP_HOST=127.0.0.1 -export CODESPACE_HTTP_PORT=8787 -# Optional. Leave unset to disable auth. Never log this value. -export CODESPACE_HTTP_TOKEN="replace-me" -cargo run -p codespace-server --bin codespace-mcp -- --transport http -``` - -Endpoint: `http://127.0.0.1:8787/mcp` - -Optional static Bearer is **HTTP experiment only**. It is not an OAuth -server. ChatGPT Custom Connectors often expect OAuth or a different auth -story; **do not assume Bearer works in ChatGPT** until a live account -check says so. - -Protocol policy: CI forces **2025-11-25** on stdio and HTTP -(`protocol_compat.rs`). **2026-07-28** is progressive enhancement and is -also forced in that file with **no** legacy fallback. Auto tests that -prefer `2026-07-28` and fall back to `2025-11-25` (`http_contract`, -`transport_contract`) do **not** replace 2025-11-25-only coverage. - -ChatGPT's `MCP-Protocol-Version` header was **not** observed. Do not -assume ChatGPT requires 2026-07-28, MRTR, Tasks, subscriptions, or -`Mcp-Name` routing. - -## ChatGPT account connection - -| Check | Result | +First establish a working local MCP integration using [installation](operations.md) and [Agent Loop integration](agent-integration.md). CodeSpace implements stdio and Streamable HTTP, but a live ChatGPT account connection is not verified by this repository's local tests. + + + + +## Local MCP clients + +Configure a stdio-capable client to launch the built `codespace-mcp` executable with absolute paths for `CODESPACE_CONFIG`, `CODESPACE_PATCH_BIN`, and optionally `CODESPACE_OPERATIONS_DB`. The exact client configuration wrapper varies by client; the executable, arguments, and environment are the portable parts. + +For an HTTP-capable client, start the configured server with `--http` and connect to `http://127.0.0.1:8787/mcp`. If `CODESPACE_HTTP_TOKEN` is set, send the matching Bearer header. Complete initialization and call `workspace_info` for a registered workspace; a successful server startup alone is insufficient. + + + +## ChatGPT-specific requirements + +OpenAI's [developer-mode documentation](https://developers.openai.com/api/docs/guides/developer-mode#how-to-use), checked on 2026-09-19, describes remote MCP apps with streaming HTTP and supported authentication modes including OAuth. It does not establish that CodeSpace's static Bearer configuration is a compatible ChatGPT authentication flow. + +CodeSpace has no OAuth authorization server. It also validates HTTP Host values from its bind configuration rather than providing a separate public-host setting. Treat public HTTPS reachability, Host handling, authentication, tool discovery, and a harmless test call as separate integration checks. Do not publish an unauthenticated writable workspace just to bypass an authentication mismatch. + +## Verification status + +| Check | Evidence or remaining work | | --- | --- | -| Local stdio `tools/list` + `workspace_info` | `cargo test -p codespace-server` | -| Local Streamable HTTP `tools/list` + `workspace_info` | `cargo test -p codespace-server` | -| ChatGPT Custom Connector against a public HTTPS URL | **Not verified.** This session has no ChatGPT account UI to complete a Custom Connector. Bearer acceptance by ChatGPT is unknown. | -| Public tunnel (ngrok/cloudflare) | **Not verified** | +| Local stdio initialization and tool calls | Repository transport and protocol tests | +| Local Streamable HTTP initialization and tool calls | Repository HTTP and protocol tests | +| Real ChatGPT account connection | Not verified; requires testing against the intended account and deployment | +| Public HTTPS/proxy configuration | Not established by loopback tests | +| ChatGPT acceptance of static Bearer | Not established; do not assume support | + +See [protocol compatibility](protocol-compatibility.md) for repository-tested versions. Do not infer ChatGPT's negotiated version or required optional MCP features from those tests. + + -## Secrets +## Credentials -Never put `CODESPACE_HTTP_TOKEN` in logs, issue comments, or tool error -text. HTTP 401 body is `{ "error": "unauthorized" }` only. +Keep tokens out of commands shared in issues, logs, and committed examples. The HTTP authentication failure response contains only `{"error":"unauthorized"}`. Account secrets and public exposure are operator responsibilities; this guide does not configure them automatically. diff --git a/docs/codex-reuse.md b/docs/codex-reuse.md index 770b185..6a2b148 100644 --- a/docs/codex-reuse.md +++ b/docs/codex-reuse.md @@ -1,412 +1,65 @@ -# Codex reuse: product vs primitive + -[English](codex-reuse.md) | [한국어](ko/codex-reuse.md) - -CodeSpace does **not** embed the Codex agent. It also does **not** -reimplement every execution mechanism from scratch. - -> CodeSpace does not avoid Codex execution code. It prevents Codex -> agent/product ownership from entering CodeSpace core. Meaning and -> authorization stay here; low-level execution comes from a pinned -> Codex subgraph. - -The point is not “write less code.” A pin bump should inherit PTY, -sandbox, hardening, and path bugfixes. CodeSpace then spends its -energy on MCP, authorization, operation identity, and the Runner -contract. - -This process is execution-only: no model, no Responses API -([execution-substrate.md](execution-substrate.md)). App Server -**protocol** is not an MCP translation target. `command/exec` **shape** -(standalone argv, handles, later TTY) may land on Runner DTOs. -`codex-exec`, `codex-core`, and App Server stay forbidden. - -```text -WHO MAY → CodeSpace Gateway - MCP contract, workspace, profile meaning, - operation/idempotency, audit, Runner trait - -HOW SAFE → pinned Codex execution subgraph - patch engine, PTY, spawn/reap, Landlock/seccomp, - process hardening, filesystem mechanics, - network enforcement -``` - -Reusing a subgraph does **not** make Codex the authorizer. Gateway still -allows; the adapter still executes. - -```text -ChatGPT / Cursor / other MCP host - │ MCP - ▼ -CodeSpace Core ← only authorization authority - ├─ MCP / workspace / profile meaning - ├─ operation_key / operation_id / persist - └─ Runner contract (CodeSpace DTOs; no Codex types) - │ - ▼ - isolated adapter workspace - │ crates/patch (codespace-patch) - │ crates/codex-runtime (codespace-codex-runtime) - │ process-hardening + UDS worker; opt-in - │ crates/pty (codespace-pty) - │ interactive spawn; no Codex types on the runner API - │ crates/file-system (codespace-fs) - │ no-follow I/O + bounded walk; PathSandbox authorizes, adapter I/O is the safety boundary - │ crates/linux-sandbox-protocol (serde handshake; SANDBOX_HELPER_PROTOCOL=1) - │ crates/linux-sandbox (codespace-linux-sandbox binary) - │ process boundary: prepare / opaque plan / run exec; Restricted net hard deny - ▼ - Codex execution subgraph (pinned) → OS -``` - -Codex is an **implementation** dependency behind the adapter, not an -architectural dependency of the Gateway. `InProcessRunner`, -`UdsRunner`, a later remote runner, or another sandbox backend -can change if Codex types never leave the adapter. - -## Unit of reuse is a subgraph - -Do not require “as narrow as `codex-apply-patch`.” Ask: - -1. Is this subgraph **cohesive execution**? -2. Do **agent / model / product** types cross the Runner boundary? -3. Can it **bypass Gateway allow**? - -A wide Cargo graph is not a reject by itself. Reimplementing PTY fd -handling, signal races, Landlock, mount escapes, or process hardening -means Codex bugfixes never arrive except by hand. Prefer: - -```text -Codex bugfix → candidate pin → adapter compile / security / parity → promotion -``` - -That is **not** “track `main`.” Release acceptance stays in -[upstream-update.md](upstream-update.md). On a pin bump, diffs in -hardening / PTY / sandbox / filesystem / network crates are an -execution/security changelog, not a silent dependency bump. - -Container isolation and host sandbox are **not substitutes**. A -container plus no-new-privs / seccomp / Landlock / network limits is -defense-in-depth. Later Environments (local container, remote Linux, -bare Linux) may share the same Linux sandbox subgraph. - -## Core vs adapter - -```text -CodeSpace core - crates/domain, policy, store, server, runner - ────────────────────────────────────────── - NO Codex types (including codex-protocol) - NO Codex crate path dependency - - -isolated adapter (crates/patch today; -crates/codex-runtime today; crates/pty today; -crates/file-system today; crates/linux-sandbox today, -binary-only) - ────────────────────────────────────────── - approved execution subgraph allowed - including transitive codex-protocol - -crates/linux-sandbox-protocol (root workspace) - ────────────────────────────────────────── - serde / serde_json only; no Codex types - SANDBOX_HELPER_PROTOCOL = 1 (not UDS / WIRE_PROTOCOL) - - -adapter boundary - ────────────────────────────────────────── - CodeSpace DTO ↔ Codex DTO -``` - -Public MCP stays `workspace_id` + relative path. Internally an adapter -may resolve: - -```text -MCP virtual path - ↓ -CodeSpace WorkspaceResolver / path scope - ↓ -Codex AbsolutePath / PathUri (adapter only) - ↓ -Runner helper -``` - -## Policy vs mechanism - -| CodeSpace owns (policy) | Prefer Codex (mechanism) | -| --- | --- | -| workspace / profile allow | PTY, UDS transport primitive | -| path permission meaning | filesystem walk / symlink mechanics | -| network permission meaning | seccomp / Landlock / hardening | -| operation approval | network enforcement (when needed) | -| Runner RPC contract | shell parse / argv construction | - -`codex-execpolicy` may parse or classify in the adapter. It is **not** -the final `allow(command)`. - -## The `apply_patch` pattern (isolation, not crate width) - -Reuse the engine. Own the service around it. Same pattern for a later -runtime adapter: - -- Pin stays [upstream-lock.md](upstream-lock.md) (`6b9826e3aa83b1a5947db50f4332cb9c65f1b340`). -- Path dependency from an **isolated** Cargo workspace, not the repo - root. Today: `crates/patch`, `crates/codex-runtime` - (`codespace-codex-runtime`), `crates/pty` (`codespace-pty`), - `crates/file-system` (`codespace-fs`), and `crates/linux-sandbox` - (`codespace-linux-sandbox` binary). The runner talks to that helper - through `crates/linux-sandbox-protocol` (serde only; root workspace - member). -- NOTICE + Apache-2.0 attribution. -- Product policy stays in front of and behind the subgraph. -- Do not file-copy a crate out of the Codex workspace. - -```text -Gateway → Runner trait → UdsRunner (opt-in) - → codespace-codex-runtime helper → Codex execution crates -``` - -Root workspace must not grow a Codex path dependency. - -Policy scan (`scripts/check-no-model-deps.sh`) is a cheap parallel CI -job **without** the Codex submodule. Cost is dominated by submodule -checkout and cargo, not this grep. - -| Zone | Paths | Cost | When | -| --- | --- | --- | --- | -| core manifests | root + `crates/{domain,policy,runner,store,server}/Cargo.toml` | tiny | crate in the update range | -| core sources | those crates’ trees | low | same | -| server tests | `tests/` | low | `crates/server` or `tests/` changed | -| adapter manifests | `crates/patch/Cargo.toml`; `crates/codex-runtime`; `crates/pty`; `crates/file-system`; `crates/linux-sandbox` | tiny | adapter in the update range; allowlist only | -| protocol crate | `crates/linux-sandbox-protocol` | tiny | no `codex-` keys; runner must not path-dep the helper library | -| upstream | `third_party/codex` | huge / false positives | never | - -Update range is `SCAN_BASE` (PR base / previous `main`). Unknown range -scans **all** core crates and adapter manifests (never skip because the -diff failed). Docs-only changes skip this job with exit 0; the rust -job still runs. +# How CodeSpace uses Codex -Core manifests forbid any `codex-` dependency key. Adapter manifests -allow only the approved subgraph (`crates/patch` today: -`codex-apply-patch`, `codex-exec-server` as apply-patch workspace -graph, `codex-utils-path-uri`, `codex-process-hardening`; -`crates/codex-runtime`: `codex-process-hardening`, `codex-uds`; -`crates/pty`: `codex-utils-pty`; `crates/file-system`: -`codex-file-system`, `codex-exec-server`, `codex-utils-path-uri`; -`crates/linux-sandbox`: `codex-linux-sandbox`, `codex-sandboxing`, -`codex-protocol`, `codex-utils-path-uri`). Sources keep the agent/model patterns -(`api.openai.com`, Responses, `codex-login`, `codex-core`, -`codex-app-server`, `async-openai`). Comments that mention a crate -name are not cargo deps. +[English](codex-reuse.md) | [한국어](ko/codex-reuse.md) -The linux-sandbox **helper lock** pins the Rama **0.3.0-alpha.4** leaf -crates (`rama-error`, `rama-macros`, `rama-utils`) as resolver guards. -The **file-system lock** (`crates/file-system`) pins the same leaves -because root still sees Rama through `codespace-fs` → `codex-exec-server` -/ `codex-protocol`. Codex pin `6b9826e` is validated against that train. -A fresh resolve can otherwise pick stable `0.3.0` for those leaves while -`rama-core` stays alpha.4. The runner does **not** path-depend the helper -crate, so helper guards do not enter the root lock via sandbox. Helper -and file-system CI `cargo clippy` / `cargo test` use `--locked`. The rust -job also checks `cargo tree -p codespace-runner` for helper-package -edges (`codespace-linux-sandbox`, `codex-linux-sandbox`). -`codex-sandboxing` / `landlock` / `seccompiler` may still appear via -`codespace-fs` → `codex-protocol`; that is not the sandbox helper graph. +CodeSpace uses selected libraries from a pinned Codex checkout to implement execution. The external agent still plans and generates code. CodeSpace owns MCP, workspace permissions, operation identity, and process management. -Do **not** wrap the standalone `apply_patch` binary as a security -boundary. Do **not** wrap Codex App Server as an internal backend. + + -## Why supervisor code still exists +## Connected components -`process_id`, stdin, terminate, and timeout converge because request -lifetime is not process lifetime. The in-process supervisor remains the -**default**. Opt-in `UdsRunner` still runs that supervisor inside -`codespace-codex-runtime`. `operation_key` / `operation_status` recover -a lost **remote MCP mutating RPC**, not a Codex thread. +| Adapter | Codex components | Current role | +| --- | --- | --- | +| `crates/patch` | `codex-apply-patch`, `codex-exec-server::LOCAL_FS`, path utilities, process hardening | V4A parsing/application in `codespace-patch` | +| `crates/codex-runtime` | `codex-process-hardening`, `codex-uds` | Optional hardened worker and Unix socket | +| `crates/pty` | `codex-utils-pty` | Terminal-backed execution for `tty: true` | +| `crates/file-system` | `codex-file-system`, `LOCAL_FS`, path utilities | Runner file I/O and bounded walks with no-follow handling | +| `crates/linux-sandbox` | `codex-linux-sandbox`, `codex-sandboxing`, `codex-protocol`, `codex-network-proxy` | Binary-only command sandbox helper and enabled-network proxy | -Pulling `codex-core` / `codex-exec` / App Server to get spawn+PTY also -pulls login, models, plugins, and rollout. That blast radius is still -rejected. +A dependency's presence is not evidence that its entire service is running. For example, file and patch adapters use `LOCAL_FS` from `codex-exec-server`; CodeSpace does not use that server as its general command backend. The Linux helper owns its proxy and sandbox translation. Public types remain CodeSpace types. -## Staged take (when those WPs exist) + + + + -Documented order. **Taken in code this WP:** process-hardening, UDS, PTY -(`crates/pty` → `codex-utils-pty`), filesystem (`crates/file-system` -→ `LOCAL_FS` / `ExecutorFileSystem`), and linux-sandbox -(`crates/linux-sandbox` binary → prepare / opaque plan / `run --plan` -Restricted `exec` / Enabled managed proxy). **Taken:** -network (`Enabled` + proxy). +## Boundaries ```text -process-hardening → PTY → UDS / path → filesystem → linux-sandbox → network +Agent → CodeSpace MCP/policy/store → Runner contract + → adapter → Codex execution library → OS ``` -Complexity and lock-in grow in that order. Pin still defines every -taken subsystem at once. - -## Candidates at pin `6b9826e` - -Judged from the pin’s `Cargo.toml` files, not from Codex `main`. - -### Reuse now (in code) - -**`codex-apply-patch`** via `crates/patch`. Parse, hunk verify, apply, -parity subset. - -**`codex-process-hardening`** via `codespace-patch` and -`codespace-codex-runtime` `pre_main_hardening()`. This hardens the -worker/helper **process**, not command sandbox. Keep it as the first -line of `main`; do not add `ctor` unless the dependency fan-out is -justified. - -**`codex-uds`** via `codespace-codex-runtime` bind. RPC stays CodeSpace. - -**`codex-utils-pty`** via `crates/pty` (`codespace-pty`). -Unix: `portable-pty`, `tokio`, `libc`. Default size 24x80. Wiring it -does **not** add a PTY MCP tool. `exec_command` has optional `tty` -(default false). Gateway still mints `process_id`. Resize stays off the -Runner/MCP surface (P1). - -**`codex-file-system`** via `crates/file-system` (`codespace-fs`). -Bounded walk, no-follow I/O through `LOCAL_FS` (`sandbox: None`). -Public types stay CodeSpace (`Path` / bytes / walk result / `FsError`). -`PathSandbox` remains the **authorizer** (logical workspace selection). -It is not the I/O safety boundary; live processes may race a pre-check. -`codespace-fs` owns race-resistant no-follow open/read/write/remove/walk -and typed errors (`SymlinkRejected`, `NotRegularFile`). MCP `read` / -`find` stay workspace-relative. - -**`codex-linux-sandbox`** via `crates/linux-sandbox` -(`codespace-linux-sandbox` binary). -([`codex-rs/linux-sandbox/Cargo.toml`](../third_party/codex/codex-rs/linux-sandbox/Cargo.toml)) - -Process boundary, not a library adapter. The runner sends -`SandboxPrepareRequest` JSON (`SANDBOX_HELPER_PROTOCOL = 1`) to -`prepare`, gets a plan **pathname** only, then spawns managed -`run --plan`. Restricted unlinks the 0600 plan and `exec`s itself with -Codex argv (same PID). Enabled starts `NetworkProxy` in the helper, sets -`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`, then spawn+waits the Codex -argv child (`--allow-network-for-proxy`; `--proxy-route-spec` is attached -by the Codex outer at runtime, not stored in the plan). `WIRE_PROTOCOL` -stays `3`. Restricted network is `--unshare-net` plus Restricted -seccomp. Enabled is isolated netns plus that managed proxy, not Codex -FullAccess host network. Runtime deps do not include `codex-core`; **dev-dependencies -do** — adapter tests must not pull that graph into the product binary. -`codex_protocol::PermissionProfile` stays inside the helper. -`codespace-runner` depends on `codespace-linux-sandbox-protocol` only. - -**Direct in the helper:** `codex-network-proxy` (`NetworkProxy` lifetime -for Enabled `run --plan`). **Transitive (allowed in the adapter):** -`codex-sandboxing`, `codex-protocol`. Not an allow engine. -Not a root-workspace dep. - -### Prefer reuse (when that WP) - -**`codex-uds`** (already in `codespace-codex-runtime`) -([`codex-rs/uds/Cargo.toml`](../third_party/codex/codex-rs/uds/Cargo.toml)) - -Unix: Tokio `fs` / `net` / `rt`. Socket primitive for the opt-in Runner -Unix-socket worker. **RPC protocol stays CodeSpace-owned**. - -**`codex-utils-absolute-path` / `codex-utils-path-uri`** - -Small path/URI layer (`dirs`, `dunce`, URL). Patch already needs them. -MCP still exposes workspace-relative paths only. - -**`codex-file-search`** -([`codex-rs/file-search/Cargo.toml`](../third_party/codex/codex-rs/file-search/Cargo.toml)) - -`ignore`, `nucleo`, Tokio. No `codex-core`. MCP stays -`find(query, workspace_id)`; the engine can move behind the adapter. - -### Conditional / active evaluation - -**`codex-shell-command`** - -Tree-sitter Bash/PowerShell, shlex, `which`. Parse / quoting / -executable resolution only. Not the allow engine. - -### Internal protocol candidate - -**`codex-exec-server-protocol`** -([`codex-rs/exec-server-protocol/Cargo.toml`](../third_party/codex/codex-rs/exec-server-protocol/Cargo.toml)) - -file-system, network-proxy, protocol, shell-command, path-uri. Later -worker DTO / adapter substrate. **Not** an MCP or `crates/domain` type. - -### Isolated-layer transitive: `codex-protocol` - -Heavy: execpolicy, http-client, network-proxy, extension items, -Landlock/seccompiler on Linux. **Forbidden in core.** Allowed in the -adapter because forbidding it forces a rewrite of file-system, -sandbox, and shell-command. `codex_protocol::PermissionProfile` must -not appear on MCP or in `crates/domain`. - -### Experimental backend (not now) - -**`codex-exec-server`** -([`codex-rs/exec-server/Cargo.toml`](../third_party/codex/codex-rs/exec-server/Cargo.toml)) - -HTTP/WS plus `codex-api`, `codex-config`, OTel, protocol, sandboxing, -PTY. Too heavy as today’s Runner backend. Not a forever reject. Later -compare UdsRunner + low-level crates vs Gateway adapter → -exec-server. Measure compile graph and upgrade cost. - -### Future Environment (not P0) - -**`codex-git-utils` / `codex-worktree`** — isolated checkout / -worktree lifecycle if Environment provisioning needs it. Pull -file-system, protocol, PTY, `gix`. Leave until that WP. - -### Code allowed, authority forbidden - -**`codex-execpolicy`** — Starlark prefix rules. Classification/parsing -in the adapter is fine. Final allow stays Gateway. - -### Reject - -**`codex-exec`** — App Server client, `codex-core`, login, config, -rollout, history. Product exec flow, not `spawn`. - -**`codex-core`** — agent loop, tools, session. +Core crates have no direct Codex dependencies. Adapter crates are separate Cargo workspaces to accommodate the pinned upstream workspace dependencies. The filesystem and PTY adapters are library dependencies of the Runner; patch and Linux sandbox operations use helper processes. An isolated Cargo workspace alone does not create a process or security boundary. -**App Server embed** (`ChatGPT → MCP adapter → Codex App Server`) — -re-imports agent infrastructure and splits authorization. +The Linux sandbox helper is binary-only. `codespace-linux-sandbox-protocol` contains its CodeSpace-owned handshake data and no Codex types. Worker UDS protocol version 3 and sandbox-helper protocol version 1 are separate contracts. -**login / model / Responses** — execution-only violation. + + + + + -**Codex session `permissionProfile` / user sandbox config as allow** — -second authorizer. +## Authority stays in CodeSpace -## What stays CodeSpace +Gateway policy decides which workspace actions are allowed. Codex execution code implements mechanisms such as no-follow file access, PTY creation, and sandbox setup. Importing Codex session permissions, login, model selection, or an agent loop would change that responsibility split and is not part of this product. -- MCP tool schemas and domain types (no `rmcp` in runner/domain; no - Codex types in core). -- Workspace registry, **meaning** of profiles, path policy. -- Write lock, shell occupancy, `WORKSPACE_BUSY` (until a scheduler WP). -- `operation_key` replay, `operation_id`, `operation_status`. -- Host/in-process process supervisor as the default; UDS worker is opt-in. -- Container lifecycle and workspace bind-mount **policy**. -- Isolated adapter workspaces (`crates/patch`, - `crates/codex-runtime` / `codespace-codex-runtime`, `crates/file-system` / `codespace-fs`, - `crates/linux-sandbox` / `codespace-linux-sandbox` binary). - Handshake types live in `crates/linux-sandbox-protocol`. +Current entrypoints do not embed `codex-core`, `codex-exec`, or Codex App Server as a product runtime. Broad transitive crate graphs are evaluated separately from runtime call sites. See [security boundaries](security-model.md). -## Next implementation WP + + + + + + + + -The next **code** work package is the remaining execution subgraph -(network: `Enabled` + proxy) behind the existing `Runner` -trait. Do not split `apply_patch` into multiple gateway-driven RPCs. +## Updates and future work -Do not default to a homegrown PTY / Landlock / seccomp stack. Take the -execution subgraph through an isolated workspace after the table above. -Pin bump is a deliberate release -([upstream-update.md](upstream-update.md)): SHA + patch parity now; -runtime-adapter build plus PTY / sandbox / process regressions when -those crates are taken. +All reused components currently come from the [same pinned revision](upstream-lock.md). Upstream fixes arrive only after an explicit pin update and validation; they are not automatically inherited. [Update checks](upstream-update.md) include each connected adapter, not only patch tests. -Domain expansion remaining (scheduler queue, approval tools) is -sequenced in [execution-substrate.md](execution-substrate.md). Live MCP -tool names stay unchanged; `exec_command` gained optional `tty`. +`codex-file-search`, shell-command parsing, worktree provisioning, and a general `codex-exec-server` backend remain candidates, not connected features. Evaluate a candidate by the execution function it supplies, its build/upgrade cost, and whether model or permission authority would cross the adapter boundary. Current user-facing limitations are listed in [Agent Loop integration](agent-integration.md). diff --git a/docs/documentation.md b/docs/documentation.md index cd05666..200769f 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -1,39 +1,41 @@ -# Public documentation and the documentation site + + +# Maintaining the documentation [English](documentation.md) | [한국어](ko/documentation.md) -English files are the editorial source. Root guides use matching -`.ko.md` files. Guides under `docs/` use `docs/ko/`. Keep reciprocal -language links. Commands, code blocks, identifiers, and support -conditions must stay the same across each pair. +The published site and repository guides share the same maintained Markdown. English is the editorial source; each maintained guide has a reviewed Korean counterpart. Improve unclear English first, then write natural Korean with the same behavior, limits, and examples. + +## Editorial review + +Check each page against current code, configuration, and tests. Separate implemented behavior, configured availability, test coverage, and actual deployment evidence. Describe what the reader can do before introducing internal implementation names. Explain necessary technical terms on first use. + +Avoid unexplained work-package numbers, past-session claims, repeated negative comparisons, and unrelated repository names. Keep dependency attribution and useful source links. Translate table explanations and navigation labels; preserve API identifiers. Korean prose should read naturally on its own, rather than mirror English word order. + +Give each fact a primary reference page and link to it elsewhere. Keep installation instructions reproducible, distinguish placeholders from runnable examples, and explain uncertain outcomes instead of implying success. Review the complete pair a second time for consistency before recording hashes. + +## Registry and compatibility + +The [registry](translations.json) maps stable document IDs, routes, navigation groups, anchors, and reviewed file hashes. Root guides use `.ko.md`; other translations live under `docs/ko/`. Keep links within the current language where a counterpart exists. + +Preserve existing routes and old heading anchors when renaming sections, using explicit compatibility anchors near the replacement section. New maintained guides require both languages and a registry entry. Hashes detect later edits; they do not prove semantic equivalence or replace editorial review. -The [document registry](translations.json) records stable IDs, -navigation groups, preserved anchors, source/translation paths, and -both reviewed file hashes. The hashes detect drift. They do not prove -semantic equivalence or human approval. Review both complete documents -before recording the pair. +After reviewing each changed pair, record its ID explicitly: -```sh +```bash +python3 -B scripts/check_docs.py record --id agent-integration python3 -B scripts/check_docs.py -python3 -B scripts/check_docs.py record --id documentation ``` -`record` is for an editor after reviewing that document pair. Select -each reviewed ID explicitly. A new maintained Markdown document needs -its Korean edition and a registry entry. CI never updates review -records automatically. +Do not refresh every hash just to silence a failure. Check that the rendered page, copied Markdown, language navigation, and preserved links still match the intended content. -## Documentation site + -The site uses VitePress 1.6.4, Node 24.21.0, and npm 11.19.0 with the -committed [npm lock](../docs-site/package-lock.json). The site -implementation is MIT, copied from docs-actions and adapted here. See -[site provenance](../docs-site/PROVENANCE.json). Project documentation -stays Apache-2.0. +## Build and preview -Do not start the Vite development server. From the repository root: +Use Node 24.21.0, npm 11.19.0, and Python 3.11 or later. CI uses Python 3.14. Set `DOCS_PYTHON` if the executable has another name. The site uses the committed VitePress lockfile; do not update dependencies as part of a wording change. -```sh +```bash npm ci --prefix docs-site --ignore-scripts npm test --prefix docs-site npm run build --prefix docs-site @@ -41,28 +43,14 @@ python3 -B docs-site/scripts/site.py check python3 -B docs-site/scripts/site.py preview ``` -The preview listens at `http://127.0.0.1:43141/CodeSpace/`. Rebuild -after edits. The preview has no hot module replacement. English is at -the site root and Korean is under `/ko/`. Maintained pages live under -`/guide/` and `/ko/guide/`. Copy Page copies the current language's -maintained Markdown. Local search stays in the browser. +Preview is `http://127.0.0.1:43141/CodeSpace/`. After edits, stop preview, rebuild, and restart preview: it serves only files matching the manifest loaded at startup. Use this verified static preview instead of the Vite development server. English is at the root and Korean under `/ko/`; guides use `/guide/` and `/ko/guide/`. Copy page uses the current language's maintained Markdown. Search runs locally in the browser. -Set `DOCS_PYTHON` when the Python executable has another name. Use -Python 3.11 or later. CI selects Python 3.14. +Check desktop and narrow layouts in both themes, including long code/table content. Exercise navigation, language switching, search, and copying. A passing registry/build check alone does not establish readability or correctness. -## Publication + -Pull requests run a read-only docs build and keep a review artifact. -They do not deploy. A `main` push or a manual `main` run packages the -same verified directory and calls the pinned docs-actions reusable -workflow. Grant only that deployment job `pages: write` and -`id-token: write`. Do not use `secrets: inherit`. +## Publication and attribution -The pin is [`.github/docs-pages-deploy.lock.json`](../.github/docs-pages-deploy.lock.json). -Adopt a new SHA only after reviewing that commit, its license scope, -and successful central CI. +PRs build a review artifact and do not deploy. A main push or manual main workflow packages the verified directory for GitHub Pages. Build metadata records the source commit and source hashes; a successful artifact upload is not proof of a live deployment. -GitHub Pages source, the `github-pages` environment, and allowing the -public reusable workflow are repository settings. They are not applied -by this documentation build. A successful local build or retained -artifact is not a live site. +The site implementation is MIT; project documentation is Apache-2.0. Preserve [site provenance](../docs-site/PROVENANCE.json), dependency notices, and licensing files. The publication workflow uses a reviewed external action recorded in [the deployment lock](../.github/docs-pages-deploy.lock.json); change it only as a separate reviewed dependency update. Repository Pages/environment settings are operator-managed, not created by this build. diff --git a/docs/error-codes.md b/docs/error-codes.md index 20a9fad..3c3e367 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -1,98 +1,57 @@ -# Error codes and transport vs execution + + +# Errors and uncertain results [English](error-codes.md) | [한국어](ko/error-codes.md) -HTTP/JSON-RPC **request id**, `operation_id`, and `process_id` are three -different identifiers. A lost HTTP response is not an execution failure. -Clients call `operation_status` (W08) instead of replaying a mutating tool. +First distinguish a transport failure from a tool result. An HTTP or connection error does not tell you whether the requested mutation ran. Patch, process, and coordination states have separate identifiers. -## Transport failures (no operation) + -These never mint `operation_id` and must not be stored as operations: +## Transport failures -| Signal | Meaning | -| --- | --- | -| TCP reset / client disconnect | Wire died. The process or patch may still be running. | -| HTTP 401 / 403 from Bearer middleware | Auth failed **before** any tool handler. | -| HTTP 404 at `/mcp` | Wrong path. | -| HTTP 408 / 502 / 503 / 504 | Transport or proxy. | +Authentication rejection before a handler creates no operation. A disconnect or proxy error can occur after dispatch, so it does not prove that no operation exists. HTTP 401/403, 404, 408, 429, and 502/503/504 are classified as transport failures by the domain helper; inspect the actual request stage and recover recorded work where possible. -`codespace_domain::classify_http_status` maps these to -`FailureClass::Transport`. `TRANSPORT_FAILURE_IS_NOT_OPERATION` is true. + -## Execution error codes (tool results) +## Tool error codes -Serialized as `SCREAMING_SNAKE_CASE` in JSON: +Errors use uppercase identifiers and a message. An `operation_id` may be present if a patch record was created before the failure. Policy, occupancy, and key-conflict failures before that point do not have one. -| Code | When | +| Code | Meaning | | --- | --- | -| `UNAUTHORIZED` | Tool-layer refusal after a valid transport (not Bearer 401) | -| `WORKSPACE_NOT_FOUND` | Unknown `workspace_id` (W04) | -| `WORKSPACE_BUSY` | Write lock held by a live shell (W08 / W10) | -| `INVALID_PATCH` | Patch parsing/validation (W06 / W09); after-hash / delete-still-present / omitted `after_version`. Rollback filesystem I/O is not this code | -| `INVALID_COMMAND` | Command request is structurally invalid and was rejected before process dispatch | -| `PROCESS_SPAWN_FAILED` | Execution backend confirmed that no managed process was established | -| `PATH_ESCAPE` | Workspace/path containment violation: a `../` or absolute request, or a `find` walk result outside `workspace.root` | +| `UNAUTHORIZED` | Action denied by tool policy | +| `WORKSPACE_NOT_FOUND` | Workspace ID is not registered | +| `WORKSPACE_BUSY` | Another mutation or live command owns the workspace | +| `INVALID_PATCH` | Patch parsing, preflight, or result verification failed; also retained in some helper/input error paths | +| `INVALID_COMMAND` | Malformed argv rejected before dispatch | +| `PROCESS_SPAWN_FAILED` | Backend confirmed no managed process was established | +| `PATH_ESCAPE` | Requested path leaves workspace scope | | `FILE_NOT_FOUND` | Target path does not exist | -| `PATH_NOT_DIRECTORY` | A path component that must be a directory is a regular file (`ENOTDIR`) | -| `FILE_OPERATION_FAILED` | Containment held, but the filesystem operation itself failed (`find` root canonicalize included) | -| `SYMLINK_REJECTED` | Symlink file or ancestor | -| `SPECIAL_FILE_REJECTED` | Device, socket, fifo | -| `ADD_FILE_EXISTS` | Add File destination already exists | +| `PATH_NOT_DIRECTORY` | A required directory component is not a directory | +| `FILE_OPERATION_FAILED` | Filesystem operation failed within the authorized scope | +| `SYMLINK_REJECTED` | A symlink path was rejected | +| `SPECIAL_FILE_REJECTED` | Device, socket, FIFO, or other non-regular target rejected | +| `ADD_FILE_EXISTS` | Add destination already exists | | `MOVE_DESTINATION_EXISTS` | Move destination already exists | -| `VERSION_CONFLICT` | `expected_versions` mismatch | -| `OPERATION_KEY_CONFLICT` | Same key, different request (W08) | -| `OPERATION_NOT_FOUND` | Unknown `operation_id` / `operation_key`, or `operation_status` did not receive exactly one of them | -| `PROCESS_NOT_FOUND` | Unknown `process_id` | -| `OUTPUT_LIMIT` | Reserved; live `read_process` drops oldest bytes instead of storing unbounded output | -| `TIMEOUT` | Managed process time limit (default 30s; `CODESPACE_PROCESS_TIMEOUT_SECS`) | -| `WORK_NOT_FOUND` | Unknown `work_id` | -| `WORK_CLOSED` | Mutating steer on a closed work | -| `INTENT_NOT_FOUND` | Unknown `intent_id` | -| `INTENT_ALREADY_CLAIMED` | Edit/cancel after the model claimed the item | -| `INTENT_NOT_EDITABLE` | State is not draft/queued | -| `INTENT_REVISION_CONFLICT` | Optimistic `revision` mismatch | -| `QUEUE_NOT_EMPTY` | Reserved; `work_finish` returns `closed: false` instead of this error | - -Apply results use `status` (`applied`, `checked`, `rejected`, -`failed_rolled_back`, `failed_partial`, `unknown`). `checked` is a -successful `check_only` preview. `rejected` is an actual refusal. A -failed apply never reports `applied`. `applied` requires disk hashes to -match the helper's claimed `after_version`. Restart leaves unfinished -rows as `unknown` and does not auto-apply. - -`exec_command` can return a successful result with -`dispatch_status=unknown` when spawn may have occurred. That is not a -transport error body. The returned `process_id` identifies the uncertain -attempt. Do not start a duplicate process. Use `read_process` or -`terminate_process` when the backend remains reachable; do not assume -unknown means the process did not start. - -`INVALID_PATCH` is no longer used for exec command validation, confirmed -process-spawn failures, or rollback filesystem I/O. - -`codespace-fs` `FsError` maps 1:1 onto product codes: -`NotFound` → `FILE_NOT_FOUND`, `NotDirectory` → `PATH_NOT_DIRECTORY`, -generic `Io` → `FILE_OPERATION_FAILED`, `SymlinkRejected` → -`SYMLINK_REJECTED`, `NotRegularFile` → `SPECIAL_FILE_REJECTED`. -`PATH_ESCAPE` is a workspace/path containment violation: a client -request that would leave scope (`../`, absolute), or a walk result that -fails `strip_prefix(workspace.root)`. `FILE_OPERATION_FAILED` means -containment held and the operation itself failed. - -`INVALID_COMMAND` is a structurally invalid argv. The gateway rejects it -before minting a `process_id` or taking a mutation lease. The runner -repeats the same check. `PROCESS_SPAWN_FAILED` means the backend -**confirmed** that no managed process was established; the gateway -releases any lease. Linux helper `prepare` / protocol / helper OS-spawn -failure is this code. After a managed helper spawn succeeds, `run --plan` -load, self-exec, or inner sandbox failure is a process exit, not this -code. Neither `INVALID_COMMAND` nor `PROCESS_SPAWN_FAILED` is -`dispatch_status=unknown`. - -After `begin` mints an `operation_id`, tool errors include that id on -`ErrorBody.operation_id`. Policy / lock / key-conflict refusals before -`begin` do not. - -`work_finish` with pending user input is **not** a transport failure. It -returns `{ "closed": false, "reason": "pending_user_input" }`. +| `VERSION_CONFLICT` | Current content does not match the expected version | +| `OPERATION_KEY_CONFLICT` | Same patch key was used with different arguments | +| `OPERATION_NOT_FOUND` | Unknown lookup ID/key, or not exactly one identifier supplied | +| `PROCESS_NOT_FOUND` | Process handle missing, expired, or stdin already closed on write | +| `OUTPUT_LIMIT` | Helper output exceeded its bound; process reads instead discard old bytes | +| `TIMEOUT` | A managed command or helper exceeded its time limit | +| `WORK_NOT_FOUND` | Unknown logical work | +| `WORK_CLOSED` | Operation requires an open work | +| `INTENT_NOT_FOUND` | Unknown user instruction | +| `INTENT_ALREADY_CLAIMED` | Instruction has already been claimed | +| `INTENT_NOT_EDITABLE` | Instruction state does not permit editing | +| `INTENT_REVISION_CONFLICT` | Instruction revision changed | +| `QUEUE_NOT_EMPTY` | Reserved code; work_finish currently returns closed:false | + +## Dispatch and completion + +`dispatch_status: unknown` is a successful exec result describing an uncertain dispatch, not proof that no process started. Use the returned process handle if reachable and avoid duplicate launches. `confirmed` acknowledges dispatch; it does not mean command success. + +Linux helper preparation/protocol/start errors occur before a managed process exists and use `PROCESS_SPAWN_FAILED`. Once the helper is running, plan load, inner sandbox, or proxy-start failure becomes process termination. There is currently no exit-code field in the public result, so a completed output stream is insufficient success evidence. + +For patches, see the [status table and rollback limits](behavior-differences.md). For retries, timeouts, missing handles, and user-instruction completion, follow [Agent Loop integration](agent-integration.md). A `work_finish` response with `closed: false` and `reason: "pending_user_input"` is an application result, not a transport failure. diff --git a/docs/execution-substrate.md b/docs/execution-substrate.md index 0f46f51..9e02878 100644 --- a/docs/execution-substrate.md +++ b/docs/execution-substrate.md @@ -1,338 +1,58 @@ -# Execution substrate + -[English](execution-substrate.md) | [한국어](ko/execution-substrate.md) +# Execution contracts -CodeSpace is an **execution-only MCP**. ChatGPT (or another MCP host) -plans and writes code. This process never calls a model, never calls -the OpenAI Responses API, and never runs an agent loop. +[English](execution-substrate.md) | [한국어](ko/execution-substrate.md) -```text -ChatGPT - 판단 / 계획 / 코드 생성 - │ MCP tools/call - ▼ -Headless execution MCP - no model, no Responses API, no agent loop - │ - patch / exec / fs / sandbox / permissions / audit - ▼ -Filesystem / OS / container / (later) remote runner -``` +An external Agent Loop owns planning, model context, and completion decisions. CodeSpace supplies deterministic tool operations and observable state. Adding an execution feature must not require an internal model call or a Codex agent session. -Take a Codex App Server idea only if it answers **yes**: + + -> Can this provide execution, permission, state, or observation -> **deterministically, with no model**? +## Policy and mechanism -If it needs prompt, context, turn, reasoning, review, or a model -catalog, leave it out. Do not translate App Server JSON-RPC into MCP. -Extract substrate; re-expose it as CodeSpace tools. +The gateway decides whether a registered workspace permits an action. Runner requests contain CodeSpace-owned types; adapters translate them into Codex execution types. Direct Codex crate dependencies and types stay out of core manifests and interfaces, although adapters can bring transitive dependencies into the build graph. -Authorization (**WHO MAY**) stays the Gateway. Safe execution (**HOW -SAFE**) is supplied by a pinned Codex **execution subgraph** behind -the Runner (implementation dependency, not an architectural one). -Core crates must not import Codex types, including `codex-protocol`. -The adapter may. Take/leave table: -[codex-reuse.md](codex-reuse.md). Pin: -[upstream-lock.md](upstream-lock.md) (`6b9826e`, `rust-v0.154.0`). -Survey notes that mention Codex `main` `4701aa4b` are **not** a pin -bump. Re-check graphs after a deliberate W13 update. +The operator registry maps `read-only` and `workspace-write` to effective permissions and chooses `restricted` or `enabled` networking. Internal policy types can represent more detailed path rules, but those globs are not a public, fully enforced permission feature. Client arguments cannot escalate permissions. -CI: `policy-scan` job runs `scripts/check-no-model-deps.sh` **without** -submodules, in parallel with `rust`. Core manifests may not declare -`codex-*` deps. Core sources keep the agent/model grep. Isolated -adapter manifests (`crates/patch`, `crates/codex-runtime`, `crates/pty`, -`crates/file-system`, `crates/linux-sandbox`) use an -**allowlist**; `crates/linux-sandbox-protocol` is a root-workspace -serde crate (no `codex-` keys). `third_party/codex` sources are never scanned. -`SCAN_BASE` limits the tree to the update range; unknown range scans -all core crates and adapter manifests. Clippy/tests still always run. -The rust job checks the pin SHA (`PIN_ONLY=1`) **before** fmt/clippy, -and `cargo tree -p codespace-runner` for sandbox-specific graph edges. + -## Invariant +## Environment and identity -| Must | Must not | +| Concept | Current use | | --- | --- | -| MCP tools for read, patch, exec, process, operations | Responses / Chat Completions client | -| Gateway as the only allow path | Codex session `permissionProfile` as allow | -| Workspace-relative MCP paths | Absolute paths on the wire | -| Process handles that outlive an MCP connection | Copy App Server “kill on connection close” | -| Isolated `crates/patch` → `codex-apply-patch` | Embed `codex-app-server` / `codex-exec` / `codex-core` | - -`read-only` / `workspace-write` stay the live MCP profiles. Richer -filesystem glob + network axes live in `crates/policy` as -`PermissionProfile`, mapped from those profiles. `process_exec` is the -Exec axis (`read-only` denies, `workspace-write` allows). Path globs are -**domain only**; live enforcement stays coarse `allow(Write|Exec)` plus -PathSandbox. Restricted network is OS-enforced when the Linux helper -probe succeeds (`workspace_info.execution.network.enforcement=enforced`). -`Enabled` is isolated netns plus a helper-owned managed proxy, not host -FullAccess. Without the helper, Enabled is `PROCESS_SPAWN_FAILED` (not -silent allow). Operators may set workspace JSON `network` to -`restricted` (default) or `enabled`; MCP profiles stay -`read-only` / `workspace-write` and `{ "network": true }` is not a -grant. The axis never grants. This is not an -import of Codex user config. - -## Four axes (target domain) - -Not all of these are MCP fields today. **Do not add `environment_id` -to live tools.** Operator JSON may register environments. Omitted -environment is the implicit local host. Unknown environment ids fail -config load. `linux-container` loads but exec/patch fail closed. - -```text -Environment where command and filesystem ops run -Workspace which tree inside that environment is in scope -PermissionProfile what that pair may do (gateway-owned) -Operation this mutating RPC (id, key, persist, audit) - ↓ -Process / Patch / FS -``` - -- **Environment** is not an agent. Local host, a Linux container, or a - later remote runner are environments. Registration is a control-plane - / operator action. The model must not supply `execServerUrl`. -- **Workspace** stays the selector on MCP (`workspace_id` + relative - path). Internally the runner may resolve to an absolute path. -- **PermissionProfile** shape (Read / Write / Deny on path, glob, or - special roots; `process_exec`; separate network axis) may follow App - Server. Globs are expressed, not live-enforced. The **engine that - grants** is CodeSpace policy. -- **Operation** is already `operation_id` / `operation_key` / - `operation_status`. Diff/audit ledger is P1, not conversation - history. - -```text -MCP virtual path - ↓ -WorkspaceResolver / CodeSpace path scope - ↓ -absolute path (internal; later Codex AbsolutePath / PathUri in the adapter) - ↓ -Runner / patch helper -``` - -## PathSandbox vs codespace-fs - -`PathSandbox` is logical authorization and workspace selection -(relative path, stay inside the root, reject `..`, reject leaf and -ancestor symlinks and special files). Its pre-check `symlink_metadata` -is **not** the I/O safety boundary. - -The shared I/O primitive is Codex `LOCAL_FS` with -`follow_symlinks: false`. `codespace-fs` is the Runner adapter for -read, find, version, mkdir, chmod, remove, and rollback. `apply_patch` -mutation goes `crates/patch` → `apply_patch_with_options` on the same -`LOCAL_FS` pin (`sandbox: None`). Helper preflight and post-hash may -still use `std::fs`. - -`sandbox: None` means OS command sandbox is not reused as a file-tool -authorizer. It does **not** mean unbounded I/O. File-tool workspace -scope, command sandbox, and network enforcement stay separate axes. - -Live `exec_command` may run while `read` / `find` are allowed -(`read_while_process_live`, `find_while_process_live`). A process can -replace a directory with a symlink between PathSandbox's lstat and the -open. Safety at that moment is no-follow `LOCAL_FS` I/O, not the -earlier lstat. - -`find` walks with upstream caps (depth 64, 10,000 directories, 50,000 -entries), then applies CodeSpace glob and the user limit. `truncated` -is true if the walk was cut or the filtered list exceeds the limit. -Hidden directories are not pruned (`prune_hidden_directories: false`). -Stopping the walk at the user limit when there is no glob is a P1 -optimization; P0 keeps the bounded full walk. - -Operator-registered `workspace.root` is the trust anchor. `find` and -ancestor checks may `canonicalize` that root. Descendants under it are -never followed. - -`codespace-fs` `FsError` maps 1:1 onto product codes. `NotFound` is -`FILE_NOT_FOUND`, `NotDirectory` is `PATH_NOT_DIRECTORY`, generic `Io` -is `FILE_OPERATION_FAILED`, `SymlinkRejected` is `SYMLINK_REJECTED`, -and `NotRegularFile` is `SPECIAL_FILE_REJECTED`. `PATH_ESCAPE` is a -workspace/path containment violation: a `../` or absolute request, or a -walk result that cannot `strip_prefix` the workspace root. `find` root -canonicalize failure is `FILE_OPERATION_FAILED` (containment held; the -operation failed). Rollback filesystem failures use the same mapping; -they are not `INVALID_PATCH`. - -## `command/exec`: shape vs crates - -App Server `command/exec` at the **pin** -([`command_exec.rs`](../third_party/codex/codex-rs/app-server-protocol/src/protocol/v2/command_exec.rs)) -is a **standalone** argv API: no thread, no turn. Fields include argv, -optional process id, tty, stdin/stdout streaming, output cap, timeout, -cwd, env, PTY size, `sandboxPolicy` / `permissionProfile`. Follow-ups: -write, resize, terminate. Streaming is `outputDelta`. - -That **shape** is on Runner DTOs today. PTY spawn is wired behind the -same `process_id` (`exec_command.tty`, default false; adapter size -24x80). `process_resize` / `tty_size` stay **P1**. Gateway fills -`cwd: WorkspaceRoot`, runner-local env defaults -(`PATH` / `HOME` / `LANG` applied in the runner process; `TERM=xterm` -for PTY only; not the gateway `PATH` or a host absolute cwd), timeout, -output cap, and a policy summary. Live MCP remains: - -```text -exec_command / write_stdin / read_process / terminate_process -``` +| Environment | Operator-selected execution location; host implemented, registered container backend unavailable | +| Workspace | Registered root selected by `workspace_id`; MCP file paths are relative | +| Permission profile | Gateway-owned meaning of allowed file and process actions | +| Operation | Persisted patch request with `operation_id` and optional idempotency key | +| Process | Server-issued handle for a command; memory-only | +| Work | Logical job and user-instruction queue; separate from a transport session | -The model learns this from MCP, not from adapter topology. -`initialize.instructions` holds global invariants. `workspace_info.execution` -(when a workspace is selected) holds effective capabilities. `exec_command` -results carry `dispatch_status`. Do not inject architecture manuals, -Codex crate graphs, or UDS wire details into the client contract. +`environment_id` is not an MCP tool argument. A network proxy URL or Codex user configuration supplied by the model does not become execution authority. -`output_combined=true` means `read_process` exposes one combined output -stream. stdout/stderr identity is not preserved. Pipe-backed processes -pump stdout and stderr independently, so relative ordering between them -is not guaranteed. PTY output is the terminal master stream. + + + -Do **not** take `codex-exec` (product exec flow) or embed App Server. -`codex-exec-server-protocol` is an internal worker-DTO candidate; -`codex-exec-server` is an experimental backend (`codex-api` / -`codex-config`) — not a forever reject -([codex-reuse.md](codex-reuse.md)). Do **not** default sandbox policy -from “the Codex user’s config.” Gateway maps an already-allowed -request onto runner DTOs. Prefer `codex-process-hardening`, -`codex-utils-pty`, `codex-uds` (transport primitive; RPC stays -CodeSpace), and `codex-file-system` under PathSandbox scope -(`crates/file-system` → `LOCAL_FS`, no-follow I/O and bounded walk). -`codex-linux-sandbox` is taken via the `crates/linux-sandbox` **binary** -(dev-dep includes `codex-core`; keep that out of the product graph). -The runner talks `prepare` / `run --plan` through -`crates/linux-sandbox-protocol`. Plus -`codex-network-proxy` when a network axis exists. A container does not -replace that subgraph. - -App Server streaming processes are connection-scoped and die when that -connection closes. CodeSpace keeps **MCP request lifetime ≠ process -lifetime**. `process_id` is server-minted and stored as application -state. Ending an MCP request does not kill a live process. The opt-in -UDS path is different: it is 1:1 Gateway ↔ worker. UDS disconnect or -gateway shutdown kills the worker (host children die). `process_id` -does not survive worker death. Runner `Replay` is a same-connection -primitive, not disconnect recovery. - -## Approval and MCP revision - -Insufficient permission is a **policy refusal** today (`ErrorBody`), -not a silent grant from `{ "network": true }` in tool args. - -Core protocol stays **MCP 2025-11-25** `tools/call` -([protocol-compatibility.md](protocol-compatibility.md)). MRTR and -Tasks are 2026-07-28 progressive enhancement. They must not become -required for exec or patch. - -When extra permission is designed later: - -1. Prefer explicit tools (`approval_create` / `approval_resolve` / - `operation_resume`) so 2025-11-25 clients work. -2. Optionally map the same state onto MRTR `input_required` for 0728 - hosts. - -Human / configured policy sits between the model request and OS exec. -No model is invoked to decide the grant. - -Long-running **non-interactive** jobs may later use MCP Tasks; -**interactive** jobs keep `process_id`. Tasks must not replace process -handles. - -## Scheduler (after the single write lock) - -Today one workspace write lock plus shell occupancy is enough. App -Server serializes by resource (exclusive vs shared read). The target -scopes are Environment, Workspace, Path, Process, Operation, Watch — -not Thread. `crates/store` now uses an in-memory resource serializer -for those keys. SQLite schema is unchanged. MVP takes request-owned -exclusive for `apply_patch` and process-owned exclusive for live shells -(`WORKSPACE_BUSY`); `ProcessExited` (or in-process exit) calls -`release_process`. Confirmed UDS worker death releases **all** -process-owned leases; a lost/ambiguous response by itself does not. -`read` / `find` stay unlocked. Shared-read is typed -only. - -## `fs/watch` and search - -Do not expose `fs/watch` as a model tool. Use it internally so an -external editor bump invalidates versions and `apply_patch` can fail -`expected_versions` / a future `STALE_READ`. - -Fuzzy search **sessions** are TUI typing UX. Keep MCP as `find` / -later `find_files(query, workspace_id, limit)`. Prefer -`codex-file-search` as the engine behind that contract; drop the -session protocol. - -## Hooks and skills - -Hooks are allowed only if they are local, deterministic, and cannot -call a model (`before_patch` policy, `after_patch` fmt, audit). A hook -that reviews code via Responses API is forbidden. - -Skills are not auto-injected into a hidden agent. If added, they are -MCP resources or prompts the **host** chooses to read. - -Downstream MCP federation (this server as MCP client) is P3: no model, -but auth and tool-name collision are expensive. - -## Take / leave (concepts) - -| Take (substrate) | Leave (agent runtime) | -| --- | --- | -| V4A parse/verify/apply | `thread/*`, `turn/*`, steer-as-turn | -| Standalone command/exec **shape** | `codex-exec` crate, App Server embed | -| PTY / UDS / linux-sandbox / hardening subgraph | Homegrown Landlock/seccomp/PTY/UDS by default | -| Filesystem mechanics under PathSandbox scope | Replacing PathSandbox wholesale; `codex-protocol` in core | -| Process manager / PTY helper | Connection-scoped process death | -| Sandbox **policy object** (gateway fills) | User Codex config as default allow | -| Permission profile **shape** in `crates/policy` | `permissionProfile` from the model or Codex session | -| Environment as exec location | Agent / account / model provider | -| Resource serialization | Thread-keyed queues | -| Internal fs/watch | Watch as an MCP tool | -| Search engine, not session RPC | Absolute-path `fs/writeFile` on the wire | -| Deterministic hooks | Hook → model | -| Operation / diff / audit | Conversation compaction, memory, review, Guardian, multi-agent, Goal | +## Execution and observation -Concept maps (do not import the types): Thread → workspace/operation -history; Turn → Operation; Interrupt → cancel; Turn diff → -`operation_diff`; Approval → policy + human; Attachment → artifact -resource. +Gateway fills workspace-root cwd, runner-local environment defaults, time/output limits, PTY choice, and policy into the internal Runner request. Only `tty` is exposed as a terminal option today. Public calls do not accept arbitrary cwd/env/timeout overrides. See [operations](operations.md) for defaults and [Agent Loop integration](agent-integration.md) for result handling. -## Roadmap (implementation later) +A workspace mutation lease prevents simultaneous patch/exec mutations. Read and find remain available while a command runs, so filesystem I/O must reject symlink races at open time rather than rely on a prior path check. Runner file operations use `codespace-fs`; patch execution uses the separate patch helper. -P0 code for this substrate is in: Runner exec DTO **shape** -(`RunnerCwd::WorkspaceRoot`, runner-local env defaults), -`PermissionProfile` (`process_exec`) and Environment in `crates/policy`, -resource serializer (request vs process owners), opt-in `UdsRunner` + -`codespace-codex-runtime` (process-hardening + UDS), isolated -`crates/pty` → `codex-utils-pty`, isolated `crates/file-system` → -`LOCAL_FS`, isolated `crates/linux-sandbox` binary → `codex-linux-sandbox` -(process boundary). -Live MCP tool **names** stay frozen; -`exec_command` has optional `tty` (default false). +UDS transport and Linux sandbox preparation have distinct protocols and failure boundaries. A partially delivered UDS mutation may yield an uncertain result; never retry it as a new mutation merely because the connection failed. The complete process and isolation rules belong in [runner isolation](runner-isolation.md). -**P0** — landed or next subgraph WPs: `codex-apply-patch` (done), exec -runtime **shape** on Runner DTOs (done), PermissionProfile domain in -`crates/policy` (done), Environment domain (operator-registered; not a -tool arg) (done), resource serializer (done), transport -(`UdsRunner`) with process-hardening + UDS (done, opt-in), PTY I/O -backend (done), filesystem mechanics under PathSandbox (done), Linux -command sandbox (done: helper process boundary, Restricted hard deny, -Enabled managed proxy). P0 execution subgraph is complete. + + + + + + -**P1** — operation state machine / diff ledger, approval fallback -tools, internal watch, richer process handles (resize, caps), -disconnect policy. +## What remains unimplemented -**P2** — `find` quality via `codex-file-search` behind the existing -MCP contract, deterministic hooks, skills as resources or prompts. +Process exit codes and explicit output-loss metadata are not exposed to MCP. PTY resize, file range/pagination arguments, durable process recovery, container/remote dispatch, approval-resume tools, and a resource queue scheduler remain absent. Richer internal types and negotiated protocol flags do not imply those features are callable. -**P3** — remote environment, MCP federation, artifact registry. +## Maintaining the boundary -The next **code** WPs are P1 (operation state machine / diff ledger) -behind the existing trait, without splitting `apply_patch` into gateway RPCs. -Sandbox / network are not a default homegrown OS -stack ([codex-reuse.md](codex-reuse.md)). +`check-no-model-deps.sh` scans core dependency declarations and selected source patterns, and checks adapter dependency allowlists. It does not scan the entire upstream source tree or prove the absence of every possible model call. CI also checks the Codex pin, adapter builds/tests, and prohibited sandbox-helper library edges from the Runner. See [upstream updates](upstream-update.md) for release validation. diff --git a/docs/index.md b/docs/index.md index 3dc913b..796e4a5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,24 +2,22 @@ [English](index.md) | [한국어](ko/index.md) -CodeSpace is a personal execution-tools MCP server. An outer client -decides what to do. This process never calls a model. It reads files, -applies Codex-format patches, and runs managed commands in a registered -workspace. +Use CodeSpace as the execution layer beneath your own coding agent. Start with a local workspace, establish an MCP connection, then add the execution and recovery rules your agent needs. -## Find your next step + -- [Getting started](../README.md) — tools, run commands, and license -- [Operations](operations.md) — install, run, logs, and recovery -- [ChatGPT connector](chatgpt-connector.md) — stdio, HTTP, and unverified account checks -- [Architecture](architecture.md) — current and target process layout -- [Execution substrate](execution-substrate.md) — no-model execution invariant -- [Protocol compatibility](protocol-compatibility.md) — MCP 2025-11-25 baseline -- [Behavior differences](behavior-differences.md) — product policy vs crate defaults -- [Security model](security-model.md) — gateway policy and trust boundaries -- [Runner isolation](runner-isolation.md) — current host exec and target Linux container -- [Error codes](error-codes.md) — transport failure vs execution error -- [Codex reuse](codex-reuse.md) — product vs primitive -- [Upstream lock](upstream-lock.md) — pinned Codex submodule -- [Upstream pin update](upstream-update.md) — deliberate release checklist -- [Documentation site](documentation.md) — bilingual registry and Pages +## Choose a guide + +| Your goal | Start here | +| --- | --- | +| Understand the product and its limits | [Getting started](../README.md) | +| Install and connect a workspace | [Operations](operations.md) | +| Build a read, edit, run, and recovery loop | [Agent Loop integration](agent-integration.md) | +| Evaluate a ChatGPT connection | [ChatGPT connection status](chatgpt-connector.md) | +| Understand module responsibilities | [Architecture](architecture.md) and [execution contracts](execution-substrate.md) | +| Check permissions and isolation | [Security model](security-model.md) and [runner isolation](runner-isolation.md) | +| Handle protocol and tool results | [Protocol compatibility](protocol-compatibility.md), [patch behavior](behavior-differences.md), [error codes](error-codes.md) | +| Maintain Codex dependencies | [Reuse scope](codex-reuse.md), [pinned revision](upstream-lock.md), [update procedure](upstream-update.md) | +| Edit or publish these guides | [Documentation maintenance](documentation.md) | + +The implementation defines supported behavior. A test description identifies coverage; it is not a claim that a particular installation or external account was tested. Read the execution contract returned by `workspace_info` for the workspace you actually use. diff --git a/docs/ko/agent-integration.md b/docs/ko/agent-integration.md new file mode 100644 index 0000000..a21d290 --- /dev/null +++ b/docs/ko/agent-integration.md @@ -0,0 +1,165 @@ +# Agent Loop 연동 + +[English](../agent-integration.md) | [한국어](agent-integration.md) + +CodeSpace는 에이전트의 실행 계층입니다. 외부 루프가 도구를 선택하고 패치를 작성하며 결과를 해석하고 완료 여부를 판단합니다. 먼저 [서버 설정과 작업 공간 등록](operations.md)을 마치세요. + +## 연결과 기능 확인 + +MCP 클라이언트 SDK로 stdio 또는 Streamable HTTP를 초기화하고, 초기화 완료 알림을 보낸 다음 `tools/list`로 도구를 조회합니다. 저장소 테스트의 기준 버전은 MCP 2025-11-25입니다. `/mcp`에 도구 인자만 일반 HTTP 본문으로 보내지 말고, MCP 메시지 형식·세션 헤더·결과 해석은 SDK를 통해 처리하세요. + +아래 예시는 완전한 JSON-RPC 메시지가 아니라 `tools/call`의 매개변수 객체입니다. SDK에 따라 도구 결과가 구조화된 데이터 또는 JSON을 담은 텍스트로 전달될 수 있습니다. MCP 오류와 도구 자체의 결과를 모두 확인한 뒤 진행하세요. + +```json +{ + "name": "workspace_info", + "arguments": { + "workspace_id": "demo" + } +} +``` + +`execution.files.*.available`, `execution.process.available`, `execution.isolation.command_sandbox`, `execution.network`를 확인합니다. 도구 목록은 제공되는 기능을, 작업 공간 정보는 해당 환경의 권한과 지원 여부를 나타냅니다. 사용 가능 표시는 작업 공간 예약을 뜻하지 않습니다. 읽기 전용 작업 공간에서는 명령을 실행할 수 없습니다. + +## 파일 읽기와 패치 + +예시는 임시 프로젝트에서 실행하세요. 연결 전에 `hello.txt`를 만들고 `hi`와 줄바꿈을 넣습니다. 먼저 파일을 읽습니다. + +```json +{ + "name": "read", + "arguments": { + "workspace_id": "demo", + "path": "hello.txt" + } +} +``` + +응답의 `version`을 보관합니다. 경로는 등록된 루트 기준 상대 경로입니다. `find`는 파일 내용이 아닌 경로 glob으로 검색합니다. `read`는 최대 1 MiB, `find`는 제한된 개수의 경로를 반환합니다. `truncated`가 참이면 전체 결과를 받지 못한 것이며, 공개 API에는 범위 읽기나 페이지 지정 인자가 없습니다. + +아래 `VERSION_FROM_READ`를 실제 `read` 응답의 버전으로 바꾸세요. V4A는 `*** Begin Patch`, `*** Update File` 같은 표식을 사용하는 Codex 텍스트 패치 형식입니다. 아래 예시는 JSON의 줄바꿈 이스케이프를 사용하는 완전한 패치입니다. + +```json +{ + "name": "apply_patch", + "arguments": { + "workspace_id": "demo", + "patch": "*** Begin Patch\n*** Update File: hello.txt\n@@\n-hi\n+hello\n*** End Patch\n", + "expected_versions": { + "hello.txt": "VERSION_FROM_READ" + }, + "operation_key": "hello-preview-1", + "check_only": true + } +} +``` + +사전 검증이 성공하면 파일을 쓰지 않고 `status: "checked"`를 반환합니다. 실제 적용 시에는 패치와 예상 버전을 유지하고 `check_only: false`와 **새 키**(예: `hello-apply-1`)를 사용하세요. 사전 검증과 실제 적용은 인자가 다른 요청이므로 같은 중복 실행 방지 키를 쓸 수 없습니다. 적용 후 파일을 다시 읽어 확인합니다. [패치 상태와 복구 한계](behavior-differences.md)도 참고하세요. + +## 명령 실행과 결과 확인 + +```json +{ + "name": "exec_command", + "arguments": { + "workspace_id": "demo", + "command": [ + "/bin/echo", + "agent-smoke" + ] + } +} +``` + +명령은 인자 배열입니다. 셸을 명시적으로 실행하지 않는 한 셸 따옴표, 파이프, `&&`는 해석되지 않습니다. 작업 디렉터리는 작업 공간 루트이며 환경변수와 제한 시간은 운영자 설정을 따릅니다. 터미널이 필요한 프로그램은 `"tty": true`로 가상 터미널(PTY)을 할당합니다. 크기는 24×80으로 고정되며 크기 변경 API는 없습니다. + +응답에는 서버가 발급한 `process_id`와 `dispatch_status`가 있습니다. `confirmed`는 실행 요청이 확인되었다는 뜻이며 **명령의 성공을 뜻하지 않습니다**. ID를 저장한 뒤 적절한 간격으로 출력을 조회하고, 매번 반환된 커서를 다음 조회에 사용합니다. + +```json +{ + "name": "read_process", + "arguments": { + "process_id": "PROCESS_ID_FROM_EXEC", + "cursor": 0 + } +} +``` + +결과에는 `chunk`, `cursor`, `eof`가 있습니다. stdout/stderr는 구분 없이 합쳐집니다. EOF는 출력 수집이 끝났다는 뜻이며 성공 종료를 나타내지 않습니다. 현재 MCP에는 종료 코드가 없고, 마지막 256 KiB만 보관하므로 앞부분이 별도 표시 없이 사라질 수 있습니다. EOF나 불완전한 로그만으로 빌드·테스트 성공을 선언하지 마세요. 신뢰할 수 있는 작업별 결과로 성공을 확인할 수 없다면 미검증으로 보고해야 합니다. + + + +다음은 위 명령의 결과를 해석하는 예시입니다. ID는 설명용이며 실제 응답의 값을 사용해야 합니다. `coordination`이 없는 호출의 도구 결과 본문만 표시했습니다. + +```json +{ + "process_id": "SERVER_ISSUED_PROCESS_ID", + "dispatch_status": "confirmed" +} +``` + +```json +{ + "process_id": "SERVER_ISSUED_PROCESS_ID", + "cursor": 12, + "chunk": "agent-smoke\n", + "eof": true +} +``` + +대화형 입력과 취소에도 같은 핸들을 사용합니다. + +```json +{ + "name": "write_stdin", + "arguments": { + "process_id": "PROCESS_ID_FROM_EXEC", + "data": "input\n" + } +} +``` + +```json +{ + "name": "terminate_process", + "arguments": { + "process_id": "PROCESS_ID_FROM_EXEC" + } +} +``` + +실행 중인 명령은 작업 공간을 점유합니다. 패치를 적용하거나 다른 명령을 시작하려면 기존 명령이 끝날 때까지 기다리거나 종료하세요. 읽기와 검색은 계속 가능합니다. 개발 서버를 오래 실행하는 작업 흐름이라면 수정 전에 서버를 멈추는 절차가 필요합니다. + +## 재시도와 복구 + +| 상황 | 에이전트가 해야 할 일 | +| --- | --- | +| 패치 응답을 받지 못함 | 원래 키 또는 알고 있는 작업 ID로 `operation_status` 조회 | +| `VERSION_CONFLICT` | 현재 파일을 읽고 새 패치 작성. 이전 패치를 강제로 적용하지 않기 | +| `OPERATION_KEY_CONFLICT` | 다른 인자에 사용된 키이므로 이전 요청 확인 | +| 패치 `unknown` 또는 `failed_partial` | 해당 파일을 확인하고 불확실성을 보고한 뒤 새 작업 여부 판단 | +| 실행 `dispatch_status: unknown` | 프로세스가 존재할 수 있음. 연결 가능하면 해당 핸들을 조회·종료하고 무조건 재실행하지 않기 | +| `WORKSPACE_BUSY` | 점유 중인 작업을 기다리거나 프로세스 취소. 빠른 반복 재시도 피하기 | +| `TIMEOUT` | 실행이 중단된 것으로 처리하고 일부 변경이 남았는지 확인 | +| 서버·worker 연결 손실 | 재연결 후 기능과 파일 상태 확인. 기존 프로세스 핸들은 복구되지 않음 | + +```json +{ + "name": "operation_status", + "arguments": { + "operation_key": "hello-apply-1" + } +} +``` + +조회 ID는 하나만 지정합니다. `operation_status`는 패치 기록을 조회하며 명령 실행을 조회하지 않습니다. 요청 ID는 전송 메시지, `operation_id`는 기록된 패치, `process_id`는 관리 중인 프로세스를 가리킵니다. ID 자체는 권한을 부여하지 않습니다. 실행을 시작한 뒤 기록된 패치 거부 상태만으로 파일이 그대로라고 판단할 수는 없습니다. 적용 후 검증에 실패했다면 파일을 확인하세요. + +## 사용자 지시 처리와 완료 + +필요하면 `workspace_id`와 제목으로 `work_open`을 호출합니다. 반환된 `work_id`를 지원하는 도구에 전달하세요. 안전한 중간 지점에서 `steer_status`, `steer_claim_next`를 호출하고, 가져온 지시를 처리한 뒤 `steer_complete`로 `done` 또는 `blocked`를 기록합니다. `coordination`의 개수 정보는 알림이며 지시 본문이 아닙니다. + +사용자는 HTTP `/inbox`로 초안을 만들고 큐에 넣습니다. 초안은 큐에 등록된 뒤에만 전달됩니다. 이 API에는 HTTP 모드가 필요하며 기본 브라우저 UI는 없습니다. 큐의 지시문도 작업 공간 권한을 바꾸지 않습니다. + +지시를 모두 처리한 뒤 작업 ID로 `work_finish`를 호출합니다. `closed: false`, `reason: "pending_user_input"`이면 완료를 선언하지 말고 남은 큐를 처리하세요. 이 호출은 작업 관리 상태를 닫을 뿐, 코드의 정확성을 인증하거나 테스트 결과를 대신하지 않습니다. + +연동 검증은 읽기 → 사전 검증 → 적용 → 다시 읽기 → 실행 → 출력 수집 → 긴 명령 취소 → 키로 패치 결과 복구 순서로 구성할 수 있습니다. 실패하는 명령과 긴 로그도 포함해 현재 결과 계약의 한계를 루프가 정확히 처리하는지 확인하세요. diff --git a/docs/ko/architecture.md b/docs/ko/architecture.md index a9b1265..e100892 100644 --- a/docs/ko/architecture.md +++ b/docs/ko/architecture.md @@ -1,313 +1,79 @@ + + + + # 아키텍처 [English](../architecture.md) | [한국어](architecture.md) -CodeSpace는 개인용 **실행 도구 MCP 서버**입니다. 바깥 클라이언트 -(ChatGPT, Cursor, 또는 다른 MCP 호스트)가 무엇을 할지 판단합니다. 이 -프로세스는 모델을 호출하지 않습니다. 파일을 읽고, 핀된 Rust 엔진으로 -Codex V4A 패치를 적용하며, 등록된 워크스페이스에서 관리형 명령을 -실행합니다. - -## 정체성 - -| 해당 | 해당하지 않음 | -| --- | --- | -| Independent MCP server | Fork of CoS or cokacremote | -| Execution-only environment + contract | Codex agent / App Server wrapper | -| File read / patch / managed exec | Internal chat, Goal/Loop, multi-agent, Responses API | -| Gateway policy + (target) OS/container isolation | Kernel sandbox equivalent to Codex CLI | - -빌린 아이디어(코드 덤프 아님): - -- cokacremote에서: 헤드리스 서버, Streamable HTTP, **요청 수명**과 - **프로세스 수명**을 분리. -- CoS에서: 승인된 워크스페이스, 엔진 전 헝크별 경로 해석, 프리플라이트, - 최선을 다하는 롤백, 분리된 도구 표면. -- Codex에서: 원본 Rust `codex-apply-patch` 파싱 / 검증 / 적용, 그리고 - 이후 Runner 뒤에 격리된 응집력 있는 **실행 서브그래프**(hardening, - PTY, UDS, path, filesystem, Linux sandbox, network). Codex는 구현 - 의존성이며 제어 평면이 아닙니다 - ([codex-reuse.md](codex-reuse.md)). - -가져오지 않은 것: Electron, Chrome 확장, ChatGPT DOM, agents spawn, -Desktop, 플러그인 마켓플레이스, TypeScript `apply-patch` 포트, -`git apply --unsafe-paths`, 독립 `apply_patch` 바이너리를 보안 경계로 -감싸기(그 경로는 sandbox `None`을 쓰고 기본적으로 심링크를 따름), -TypeScript MCP 게이트웨이, 폐기된 `native/patch-worker` 트리, 또는 어떤 -아웃바운드 모델 클라이언트. 실행 전용 불변식: -[execution-substrate.md](execution-substrate.md). - -## 현재 배치 - -MVP 기본은 **호스트 프로세스 하나**입니다: `codespace-mcp`와 프로세스 -내부 `Runner`. `exec_command`는 compose로 디스패치되지 않습니다. -[`deploy/compose.yml`](../../deploy/compose.yml)은 격리 **픽스처**일 -뿐입니다. 선택적 Unix 소켓 전송(`CODESPACE_RUNNER=uds`)은 CodeSpace -JSON으로 `codespace-codex-runtime`과 대화합니다. 그것은 Linux 격리가 -아니고 기본값도 아닙니다. - -```text -CURRENT - -MCP Client - │ - ▼ -codespace-mcp (host gateway) - ├─ policy / store / coordination / operation persist - ├─ structured logging (stderr tracing) - │ - │ Runner execution DTO - │ (command/exec shape; no work_id / operation_id / coordination) - ▼ -RuntimeBackend - ├─ default: InProcessRunner - └─ opt-in: UdsRunner (CODESPACE_RUNNER=uds, 1:1) - │ length-prefixed CodeSpace JSON (protocol 1, Hello, request_id rrpc-…) - ▼ - codespace-codex-runtime (게이트웨이 RuntimeProcess가 소유) - ├─ unique 0700 dir + runner.sock (bind only; 부모 chmod 없음) - ├─ codex-process-hardening (main 첫 줄, command sandbox 아님) - ├─ 연결 끊김 / 게이트웨이 종료 → 워커 종료 (호스트 자식도 죽음) - └─ InProcessRunner 하나 - ├─ read / find / version (PathSandbox → codespace-fs) - ├─ apply_patch (one transaction) - │ expected versions → preflight → snapshot - │ → helper apply → verify → rollback - │ │ JSON stdin/stdout - │ ▼ - │ codespace-patch (host child) - │ └─ Codex Rust crate in-process - └─ exec / stdin / read / terminate - └─ host process (tokio::process::Command, - cwd = workspace root, env from runner-local defaults) - -deploy/compose.yml - └─ isolation fixture only; not connected to exec_command -``` - -```text -MCP JSON → domain params → gateway (policy/store) → Runner DTO → RuntimeBackend - │ - └─ rmcp / JsonSchema stay on MCP types, not on runner DTOs -``` - -게이트웨이는 **어느 워크스페이스에서 무엇을 해도 되는지**를 소유합니다. -토큰, 서버 설정, 워크스페이스 레지스트리, 환경, operations 데이터베이스가 -여기에 있습니다. MCP 파라미터를 러너 DTO로 매핑하며 -`ExecCommandParams`를 러너에 넘기지 **않습니다**. 도구에는 여전히 -`environment_id`가 없습니다. - -`crates/runner`는 `Runner` 트레이트, 실행 DTO, `InProcessRunner` -(파일시스템, `apply_patch` 트랜잭션 하나, 호스트 프로세스 감독), -`UdsRunner`(Unix 소켓 클라이언트), compose 픽스처 검사를 -소유합니다. 기본 백엔드는 프로세스 내부입니다. 워커 바이너리는 격리된 -`crates/codex-runtime` / `codespace-codex-runtime`입니다. - -`codespace-patch`는 제품 헬퍼 프로세스이며, 업스트림 독립 `apply_patch` -바이너리가 아니고 `native/patch-worker`도 아닙니다. Runner ↔ 헬퍼는 -JSON stdin/stdout입니다. Codex 자체는 **그 헬퍼 안에서** 프로세스 -내부로 실행됩니다. - -MVP에는 단일 인스턴스로 충분합니다. SQLite는 **패치 작업**과 -works/intents를 저장합니다. 프로세스 핸들과 자원 잠금(워크스페이스 -배타 쓰기 / 셸 점유)은 메모리에 있습니다. 메시지 브로커는 없습니다. - -게이트웨이 단위 시험은 macOS 개발 호스트에서 실행할 수 있습니다. Linux -컨테이너는 **목표** 격리 OS이며 현재 exec 경계가 아닙니다. 등록된 -`linux-container` 환경은 닫힌 실패입니다(`UNAUTHORIZED`). +CodeSpace는 에이전트의 판단과 작업 공간의 실행을 분리합니다. MCP 서버가 권한과 작업 기록을 관리하고, Runner가 허용된 파일·프로세스 작업을 수행합니다. Codex 라이브러리는 어댑터 뒤에서 사용합니다. -## 목표 배치 + + -이후 러너 분리는 양쪽 모두 Rust로 남습니다. Codex를 호출하려고 -TypeScript 게이트웨이나 `native/patch-worker`를 다시 들이지 마세요. +## 현재 실행 구조 ```text -TARGET - -MCP Client - │ - ▼ -Gateway - │ authorized RunnerRequest - │ (policy, operation_key replay, write lock, - │ dispatch, result persistence) - ▼ -Runner process boundary - │ - ├─ filesystem - ├─ patch transaction (one runner-side operation) - └─ process supervisor - │ - ▼ - isolated Linux workspace -``` - -목표 **도메인**(실제 MCP 필드 아님): Environment(어디), Workspace(무엇), -PermissionProfile(해도 되는지), Operation(이 RPC). 도구에 -`environment_id`를 넣지 마세요. 운영자 설정은 환경을 등록할 수 -있습니다. -[execution-substrate.md](execution-substrate.md)를 보세요. - -Unix 소켓 **전송**(`UdsRunner`)은 기존 `Runner` / `InProcessRunner` -타입 뒤에 선택적으로 있습니다. Host + UDS는 같은 호스트이며 Linux -격리를 주장하지 않습니다. `LinuxContainer`는 닫힌 실패입니다 -(`UNAUTHORIZED`, `operation_id` 없음). 패치 적용을 게이트웨이가 -구동하는 여러 RPC로 **쪼개면 안 됩니다**. - -```text -Runner.apply_patch(request) - expected versions → path policy → preflight → snapshot - → Codex apply → after-version verify → rollback on failure +외부 Agent Loop + → MCP 서버: 등록 정보, 권한, 패치 기록, 작업·지시 큐 + → Runner + ├─ InProcessRunner (기본값) + └─ UdsRunner → codespace-codex-runtime → InProcessRunner + ├─ 파일 작업 → codespace-fs + ├─ 패치 처리 → codespace-patch + └─ 프로세스 관리 + ├─ 파이프 / codespace-pty + └─ 사용 가능한 Linux 도우미 → 샌드박스 / 관리 프록시 ``` -게이트웨이는 인가, `operation_key` 재실행, 쓰기 잠금, 디스패치, -영속을 유지합니다. 기본은 `InProcessRunner`입니다. 선택적 -`CODESPACE_RUNNER=uds`는 비공개 Unix 소켓(unique 0700 leaf, -`runner.sock`)을 쓰며 compose 픽스처가 아닙니다. 게이트웨이가 워커를 -1:1로 소유합니다(`RuntimeProcess`, `kill_on_drop`). 재연결은 없습니다. -살아있는 소켓은 connect-probe로 확인하고 leftover는 -`ConnectionRefused`일 때만 unlink합니다. 러너 `Replay`는 같은 연결에서만 -동작합니다. - -Sandbox, PTY, 네트워크 격리는 **“기본적으로 Codex OS 공학을 -재구현”이 아닙니다.** `crates/patch`와 `crates/codex-runtime` / -`codespace-codex-runtime`과 같은 패턴으로, Runner 뒤에 격리된 응집력 -있는 실행 서브그래프를 선호합니다. 이 WP는 `codex-process-hardening`과 -`codex-uds`를 가져옵니다. Codex 타입은 어댑터에 남습니다. App Server나 -`codex-exec`를 넣지 마세요. `codex-exec-server`는 미래 측정이며 현재 -백엔드가 아닙니다. - -## 프로토콜 호환성 - -핵심 실행은 **stdio**와 **Streamable HTTP `/mcp`** 위의 **MCP -2025-11-25**입니다. 필수 원시 연산은 `initialize`, `tools/list`, -`tools/call`입니다. HTTP의 스펙 하한은 2025-03-26(Streamable HTTP가 -존재)이며, CI는 2025-11-25를 핀합니다. **2026-07-28은 점진적 향상 -전용입니다.** - -핵심은 MRTR, Tasks, subscriptions, `Mcp-Name` 라우팅, 또는 애플리케이션 -상태로서의 0728 무상태 수명주기를 요구하면 안 됩니다. 인증은 선택적 -Bearer 미들웨어로 남고, 디스패치는 `/mcp` → rmcp 도구입니다. -`ProtocolVersion`과 `NegotiatedFeatures`는 `crates/server`에만 있습니다. - -2024-11-05 HTTP+SSE는 목표가 아닙니다. 전체 매트릭스: -[protocol-compatibility.md](protocol-compatibility.md). +UDS worker와 기본 러너는 모두 같은 호스트에서 실행합니다. 두 방식 모두 명령 시작 시 Linux 샌드박스를 적용할 수 있습니다. Compose의 컨테이너 실험 구성으로 실행을 전달하는 경로는 없습니다. -## MVP 도구 + + + -실행 도구: +## 구성 요소의 책임과 상태 -| 도구 | 역할 | +| 구성 요소 | 책임 | | --- | --- | -| `workspace_info` | Identity, selector metadata, 그리고 실제 execution 계약 (자격 증명이 아님) | -| `read` | File contents + version | -| `find` | Relative-path search | -| `apply_patch` | Codex V4A only | -| `exec_command` | Start a managed **host** process | -| `write_stdin` | Write to a managed process | -| `read_process` | Cursor-based output | -| `terminate_process` | Kill a server-issued handle | -| `operation_status` | Recover by `operation_id` **or** `operation_key` | +| `server` | MCP 전송, HTTP 인증·inbox, 요청 검증과 실행 연결 | +| `domain` | CodeSpace 도구 인자·결과, ID, 오류·실행 타입 | +| `policy` | 등록 경로, 환경, 권한 프로필, 네트워크 정책 | +| `store` | SQLite의 패치 작업·논리적 작업·사용자 지시, 메모리의 점유 상태 | +| `runner` | 실행 데이터 타입, 파일 범위, 패치 처리, 프로세스 관리, UDS 통신 | +| 분리된 어댑터 | Codex 패치·PTY·파일 시스템·worker 보호와 소켓·Linux 샌드박스 구현 연결 | -조정 도구(일반 `tools/call`, MCP 2025-11-25 1급): +패치 작업과 작업·지시 큐는 SQLite 파일을 설정한 경우에만 재시작 후 유지됩니다. 프로세스 핸들과 점유 상태는 메모리에만 있습니다. `operation_status`는 명령 실행을 조회하지 않습니다. 전송 요청 ID, 패치 작업 ID, 프로세스 ID, 작업 ID, 지시 ID는 서로 다른 대상을 가리킵니다. -| 도구 | 역할 | -| --- | --- | -| `work_open` | Mint a `work_id` for one logical job | -| `steer_status` | Counts only; no intent bodies | -| `steer_claim_next` | Atomically claim one queued item | -| `steer_complete` | Mark claimed intent done or blocked | -| `work_finish` | Close only if the queue is drained | - -사용자는 MCP가 아니라 HTTP `/inbox`에서 초안을 편집하고 큐 항목을 -재정렬합니다. 의도 본문은 지시이지 능력이 아닙니다. + + -내부 모델 호출 도구는 없습니다. `git_apply_patch`는 MVP 밖입니다. -오류 코드와 전송 대 실행 규칙: [error-codes.md](error-codes.md). -Linux 격리 픽스처: [runner-isolation.md](runner-isolation.md). -Codex 제품 대 프리미티브: [codex-reuse.md](codex-reuse.md). -실행 전용 기반: [execution-substrate.md](execution-substrate.md). +## 패치 처리 과정 -## ID +게이트웨이는 작업 공간 권한을 확인하고 쓰기 점유를 확보한 다음 작업 키를 검사합니다. 이후 Runner에 패치 요청 하나를 전달하고 결과를 기록합니다. Runner는 예상 버전 확인, 사전 검증, 파일 스냅샷 저장, 패치 도우미 호출, 실제 디스크 해시 검증을 수행합니다. -HTTP/JSON-RPC 요청 id, `operation_id`, `operation_key`, `process_id`, -`work_id`, `intent_id`, 러너 `request_id`(`rrpc-…`)는 서로 다른 -식별자입니다. 잃어버린 HTTP 응답은 실행 실패가 아닙니다. 유실된 UDS -`apply_patch` 응답은 `rejected`가 아니라 `unknown`으로 기록됩니다. -클라이언트는 변경 도구를 재실행하는 대신 `operation_status`를 -호출합니다. +도우미의 적용 호출이 실패하면 파일별 스냅샷 복원을 시도합니다. 현재 적용 후 검증 오류는 이 복원 분기를 거치지 않고 반환됩니다. 따라서 파일 시스템 전체의 원자적 트랜잭션을 보장하지 않습니다. 클라이언트 처리에 미치는 영향은 [패치 동작](behavior-differences.md)에 설명합니다. -```text -Workspace (workspace_id) - └── Work (work_id) - ├── Operation (operation_id) - ├── Process (process_id) - └── User Intent Queue (intent_id) -``` +## 프로세스 수명 -`workspace_id`와 `work_id`는 **선택자**이며 인가 증명이 아닙니다. -클라이언트 인자 `approved: true`와 `user_id`는 무시됩니다. 사용자 의도 -텍스트는 권한 프로필을 올리지 않습니다. +MCP 요청이 끝나도 관리 중인 프로세스는 유지됩니다. 클라이언트는 `process_id`로 후속 호출을 수행합니다. 서버 재시작 후에는 핸들이 사라집니다. UDS 모드에서는 게이트웨이가 worker를 관리하므로 내부 연결 종료나 서버 종료 시 worker와 자식 프로세스가 종료되며 재접속은 지원하지 않습니다. [러너 격리](runner-isolation.md)에서 전송 경계와 격리 경계를 구분해 설명합니다. -## 패치 적용 파이프라인 + + + + -게이트웨이는 인가, `operation_key` 재실행, 쓰기 잠금, 영속을 -유지합니다. `InProcessRunner.apply_patch`는 실행 트랜잭션을 한 번의 -호출로 돌립니다. 게이트웨이는 이를 프리플라이트 / 스냅샷 / 적용 RPC로 -쪼개지 않습니다. +## 확장 시 유지할 경계 -```text -validate request - → auth + workspace policy - → operation_key replay / conflict - → workspace write lock - → Runner.apply_patch - expected_versions - → full preflight (no writes) - → save rollback snapshot - → original engine apply (`apply_patch_with_options`) - → verify disk hash == helper claimed after_version - → rollback on failure - → persist operation status (gateway fills operation_id) - → MCP response -``` +핵심 계층은 Codex 타입을 직접 가져오지 않습니다. 어댑터가 여러 Codex 실행 구성 요소에 의존할 수는 있지만 게이트웨이가 Codex 에이전트가 되는 것은 아닙니다. 실행 환경은 운영자가 설정하고, MCP 클라이언트는 등록된 작업 공간만 선택합니다. 컨테이너 실행, 원격 러너, 승인 후 재개 도구, 자원 스케줄러는 아직 구현되지 않았습니다. -`crates/patch`(`codespace-patch` 안)는 `parse_patch`, 그다음 제품 정책, -그다음 `apply_patch_with_options`를 **프로세스 내부에서** 호출합니다. -독립 `apply_patch` 바이너리를 감싸지 않고 파서를 재구현하지 않습니다. +새 전송 방식을 추가하더라도 패치 처리는 하나의 Runner 호출로 유지합니다. Codex 사용자·세션 권한을 실행 허용의 근거로 가져오지 않고 게이트웨이에서 결정합니다. 현재 불변 조건은 [실행 계약](execution-substrate.md), 연결된 어댑터는 [Codex 재사용 범위](codex-reuse.md)에 정리되어 있습니다. -`apply_patch`는 `git apply`로 조용히 폴백하지 않습니다. 상태 값은 -`applied` / `checked` / `rejected` / `failed_rolled_back` / -`failed_partial` / `unknown`입니다. `checked`는 성공한 `check_only` -미리보기(쓰기 없음)입니다. `rejected`는 실제 거절입니다. 전송이 모호하면 -(디스패치 후 소켓 끊김) `unknown`으로 끝내며 `rejected`로 저장하면 안 -됩니다. after-version 검증 없는 성공 문구는 금지입니다. - -롤백은 `git reset --hard`를 쓰면 안 되고, 파일별 복원 대신 디렉터리 -트리 전체를 덮어쓰면 안 됩니다. - -## 저장소 배치 - -```text -Cargo.toml workspace root -crates/server/ bin codespace-mcp: rmcp stdio + Streamable HTTP + /inbox -crates/domain/ workspace, capabilities, operation, errors (no rmcp) -crates/policy/ registry, PermissionProfile, Environment -crates/patch/ Codex adapter + codespace-patch helper (own workspace) -crates/codex-runtime/ isolated worker: hardening + UDS + InProcessRunner -crates/store/ SQLite operations, works, intents; in-memory resource locks -crates/runner/ Runner trait + execution DTOs, PathSandbox, patch transaction, host supervisor, UdsRunner, fixture checks -crates/file-system/ isolated Codex FS adapter (codespace-fs): no-follow I/O + bounded walk -third_party/codex/ git submodule, pinned revision (W06) -tests/{security,recovery,e2e}/ -docs/ including operations.md (W12), codex-reuse.md, - execution-substrate.md (W17) -deploy/ unprivileged Linux isolation fixture -``` + + + + -## 초기 범위 밖 +## 도구와 프로토콜 참고 -브라우저 확장, ChatGPT DOM 자동화, Goal/Loop, 멀티 에이전트, Desktop -제어, 플러그인 마켓플레이스, 완전한 OAuth 서버, 자동 unified-diff 변환, -Codex App Server RPC 전체 전달, 내부 모델 호출, TypeScript MCP SDK, -`native/patch-worker`. +호출할 도구와 연동 예시는 [Agent Loop 연동](agent-integration.md)을, 버전 협상과 전송 테스트는 [프로토콜 호환성](protocol-compatibility.md)을 참고하세요. 아키텍처 문서에 도구 명세를 중복해서 관리하지 않습니다. diff --git a/docs/ko/behavior-differences.md b/docs/ko/behavior-differences.md index 8344a33..4bf0f66 100644 --- a/docs/ko/behavior-differences.md +++ b/docs/ko/behavior-differences.md @@ -1,60 +1,49 @@ -# 동작 차이 + + + +# 패치 동작과 복구 [English](../behavior-differences.md) | [한국어](behavior-differences.md) -제품 정책은 “`codex-apply-patch`가 기본으로 하는 일”이 **아닙니다**. -`crates/patch`는 원본 파서와 적용 함수를 프로세스 내부에서 호출한 뒤, -게이트웨이/러너가 크레이트가 받아들일 수 있는 작업을 거절합니다. - -| 주제 | Codex 라이브러리 / 독립 기본값 (0.154 후보) | CodeSpace 제품 | -| --- | --- | --- | -| Paths from the model | 호스트 절대 경로를 받을 수 있음. 프로세스 cwd 기준으로 상대 해석 | **상대 경로만**, 등록된 워크스페이스 루트 안에서 해석 | -| Symlinks | 적용 옵션이 follow / keep going 가능 | 심링크 파일과 심링크 탈출을 **거절** | -| Special files | 제품 게이트가 아님 | 디바이스, 소켓, fifo를 **거절** | -| Add File | 헝크에 따라 기존 경로와 상호작용할 수 있음 | 목적지가 이미 있으면 Add File을 **거절** | -| Move destination | 헝크가 그렇게 말하면 엔진이 적용할 수 있음 | 이동 목적지가 이미 있으면 **거절** | -| Newlines | 여러 모드가 있음. 가정하지 말 것 | **Preserve-newline 모드 선호**. 패리티는 같은 모드 사용 | -| `git apply` | V4A 엔진이 아님. 다른 제품은 가끔 폴백 | **조용한 git-apply 폴백 없음** | -| Sandbox | 독립 `apply_patch`는 sandbox `None` 사용 | 패치 크레이트는 샌드박스가 **아님**. 지금은 게이트웨이 정책 + PathSandbox. Linux 컨테이너가 **목표** | -| Unified diff | 다른 곳의 다른 도구 | MVP 밖(`git_apply_patch`는 나중, 절대 자동 변환하지 않음) | -| Rollback | 크레이트에 N/A | 파일 스냅샷 복원. `git reset --hard` **없음** | +CodeSpace는 Codex V4A 패치를 받으며 고정된 라이브러리를 호출하기 전에 작업 공간 규칙을 적용합니다. 라이브러리가 지원하는 옵션이 곧 서비스에서 허용하는 동작은 아닙니다. + + ## 패치 요청 계약 -```json -{ - "workspace_id": "demo", - "patch": "*** Begin Patch\\n*** Update File: src/config.ts\\n...", - "expected_versions": { - "src/config.ts": "sha256:" - }, - "operation_key": "change-timeout-001", - "check_only": false -} -``` - -- `expected_versions` 값은 `read`의 내용 버전이거나, 아직 없어야 하는 - 파일의 `"absent"`입니다. Move는 출발지와 목적지를 검증합니다. -- `operation_key`는 멱등이지 능력 토큰이 아닙니다. -- `check_only: true`는 모든 대상 파일을 바이트 단위로 그대로 두고 - `status: "checked"`를 반환해야 합니다. -- 성공한 적용은 `files`(경로 목록)와 함께 `path`, `before_version`, - `after_version`, `kind`(`add` / `update` / `delete` / `move`)를 담은 - `changes`를 반환합니다. `applied`는 헬퍼가 주장한 해시가 새로 읽은 - 디스크 해시와 일치한다는 뜻입니다. - -결과 `status`: `applied` | `checked` | `rejected` | -`failed_rolled_back` | `failed_partial` | `unknown`. - -## 쓰기 잠금 - -`workspace-write` 셸은 변경하는 점유자입니다. 살아 있는 동안 그 -워크스페이스의 다른 변경 패치/exec 작업은 막히거나(`WORKSPACE_BUSY`) -문서화된 큐에 따라 기다립니다. 제품은 셸이 워크스페이스 파일을 지울 수 -없다고 가장하지 않습니다. - -## 전송 - -stdio와 Streamable HTTP는 **같은** 도구 스키마를 노출합니다. 선택적 정적 -Bearer는 HTTP 실험 전용이며, 실제 계정 검사가 그렇게 말하기 전에는 -ChatGPT Custom Connector를 만족한다고 **가정하지 않습니다**. +상대 경로와 `read`가 반환한 버전을 사용합니다. `absent`는 대상 파일이 없어야 한다는 뜻입니다. 이동 전 파일 상태를 확인하려면 원본과 대상 경로의 예상 버전을 모두 지정하세요. [사전 검증·적용 예시](agent-integration.md)에서 서로 다른 두 요청에 작업 키를 어떻게 사용하는지 설명합니다. + +| 규칙 | CodeSpace 동작 | +| --- | --- | +| 경로 | 등록 루트 안에서 해석하며 절대 경로와 범위 이탈 거부 | +| 심볼릭 링크·특수 파일 | 심볼릭 링크 경로와 장치·소켓·FIFO 거부 | +| 추가·이동 대상이 이미 존재 | 기존 대상을 덮어쓰지 않고 거부 | +| 줄바꿈 | Codex의 `PreserveLineEndings` 사용. 선택된 동작 일치 사례를 테스트 | +| 패치 형식 | V4A만 지원. unified diff 자동 변환이나 `git apply` 대체 실행 없음 | +| 사전 검증 | `check_only: true`로 파일을 쓰지 않고 검사. 예약이나 이후 적용 성공 보장은 아님 | + +## 결과와 복구 한계 + +| 상태 | 의미와 다음 행동 | +| --- | --- | +| `checked` | 사전 검증 성공. 실제 적용에는 새 작업 키 사용 | +| `applied` | 적용 후 디스크 해시가 도우미가 보고한 값과 일치 | +| `rejected` | 요청 거부 또는 오류 기록. 오류 내용과 실행이 이미 시작되었는지 확인 | +| `failed_rolled_back` | 도우미 적용 실패 후 스냅샷 복원 완료로 보고됨 | +| `failed_partial` | 도우미 적용 실패 후 복원이 불완전함. 파일 확인 필요 | +| `unknown` | 최종 결과를 알 수 없음. 재시도 전에 파일과 기록 확인 | + +Runner는 대상 파일을 스냅샷으로 저장하고 도우미의 적용 호출이 실패하면 복원합니다. 그러나 도우미가 성공 응답을 보낸 뒤 검증에서 오류가 발생하면 현재는 복원 전에 반환됩니다. 게이트웨이는 파일이 변경되었어도 그 오류를 `rejected`로 기록할 수 있습니다. 모든 거부 결과를 파일 변경이 없었다는 증거로 해석하지 마세요. 비정상 종료 후에도 자동 스냅샷 복원이나 작업 재실행은 하지 않습니다. `git reset --hard`는 사용하지 않습니다. + +성공 결과에는 영향을 받은 `files`와 변경 종류·확인 가능한 전후 해시를 담은 `changes`가 있습니다. 해시는 관측한 파일을 설명하며 저장소 커밋이나 빌드 성공을 뜻하지 않습니다. + + + + + + +## 쓰기 점유와 전송 + +명령 하나가 실행 중이면 같은 작업 공간의 다른 패치·명령은 `WORKSPACE_BUSY`로 거부됩니다. 대기 큐는 없습니다. 읽기와 검색은 가능합니다. 패치 작업 키는 저장된 요청과 인자가 일치할 때만 이전 결과를 재사용합니다. 응답이 불확실한 상태에서 새 키를 선택하면 변경을 중복 적용할 수 있습니다. + +stdio와 Streamable HTTP의 도구 스키마는 같습니다. 연결 실패만으로 변경 작업이 실행되었는지 판단할 수 없습니다. [오류 코드](error-codes.md)와 [복구 규칙](agent-integration.md)을 참고하세요. diff --git a/docs/ko/chatgpt-connector.md b/docs/ko/chatgpt-connector.md index 66e39b8..80bf6f7 100644 --- a/docs/ko/chatgpt-connector.md +++ b/docs/ko/chatgpt-connector.md @@ -1,75 +1,47 @@ -# ChatGPT 커넥터 실험 + + + +# ChatGPT 연결 상태 [English](../chatgpt-connector.md) | [한국어](chatgpt-connector.md) -CodeSpace는 Rust `rmcp` 프로세스입니다. **stdio**와 **Streamable HTTP**를 -말하며 같은 실제 도구를 제공합니다(`workspace_info`, `read`, `find`, -`apply_patch`, `operation_status`, `exec_command`, `write_stdin`, -`read_process`, `terminate_process`, `work_open`, `steer_status`, -`steer_claim_next`, `steer_complete`, `work_finish`). 사용자 초안은 MCP가 -아니라 HTTP `/inbox`입니다. `exec_command`는 현재 호스트 프로세스입니다. -compose 파일은 격리 픽스처이며 ChatGPT 연결 경로가 아닙니다. - -핵심 프로토콜 기준은 **MCP 2025-11-25**입니다. -[protocol-compatibility.md](protocol-compatibility.md)를 보세요. - -## 로컬 stdio (cargo test로 검증) - -```bash -cargo run -p codespace-server --bin codespace-mcp -- --transport stdio -``` - -Cursor 스타일 MCP 설정: - -```json -{ - "mcpServers": { - "codespace": { - "command": "cargo", - "args": ["run", "-p", "codespace-server", "--bin", "codespace-mcp", "--", "--transport", "stdio"], - "cwd": "/absolute/path/to/CodeSpace" - } - } -} -``` - -## 로컬 Streamable HTTP (실험) - -```bash -export CODESPACE_HTTP_HOST=127.0.0.1 -export CODESPACE_HTTP_PORT=8787 -# Optional. Leave unset to disable auth. Never log this value. -export CODESPACE_HTTP_TOKEN="replace-me" -cargo run -p codespace-server --bin codespace-mcp -- --transport http -``` - -엔드포인트: `http://127.0.0.1:8787/mcp` - -선택적 정적 Bearer는 **HTTP 실험 전용**입니다. OAuth 서버가 아닙니다. -ChatGPT Custom Connector는 종종 OAuth나 다른 인증 이야기를 기대합니다. -실제 계정 검사가 그렇게 말하기 전에는 **ChatGPT에서 Bearer가 동작한다고 -가정하지 마세요**. - -프로토콜 정책: CI는 stdio와 HTTP에서 **2025-11-25**를 강제합니다 -(`protocol_compat.rs`). **2026-07-28**은 점진적 향상이며 그 파일에서 -레거시 폴백 **없이** 강제됩니다. `2026-07-28`을 선호하고 `2025-11-25`로 -폴백하는 자동 시험(`http_contract`, `transport_contract`)은 2025-11-25 -전용 커버리지를 **대체하지 않습니다**. - -ChatGPT의 `MCP-Protocol-Version` 헤더는 **관찰되지 않았습니다**. ChatGPT가 -2026-07-28, MRTR, Tasks, subscriptions, 또는 `Mcp-Name` 라우팅을 요구한다고 -가정하지 마세요. - -## ChatGPT 계정 연결 - -| 검사 | 결과 | +먼저 [설치](operations.md)와 [Agent Loop 연동](agent-integration.md)에 따라 로컬 MCP 연결을 확인하세요. CodeSpace는 stdio와 Streamable HTTP를 구현하지만 저장소의 로컬 테스트로 실제 ChatGPT 계정 연결까지 검증한 것은 아닙니다. + + + + + + +## 로컬 MCP 클라이언트 + +stdio를 지원하는 클라이언트가 빌드된 `codespace-mcp`를 실행하도록 설정하고, `CODESPACE_CONFIG`, `CODESPACE_PATCH_BIN`, 필요하면 `CODESPACE_OPERATIONS_DB`를 절대 경로로 전달하세요. 클라이언트별 설정 파일 형식은 다르지만 실행 파일, 인자, 환경변수는 공통으로 필요한 항목입니다. + +HTTP 클라이언트에서는 설정된 서버를 `--http`로 시작하고 `http://127.0.0.1:8787/mcp`에 연결합니다. `CODESPACE_HTTP_TOKEN`이 설정되어 있으면 같은 값의 Bearer 헤더를 보내세요. 초기화를 마친 뒤 등록된 작업 공간으로 `workspace_info`를 호출합니다. 서버가 시작되었다는 사실만으로 연결 검증이 끝나지는 않습니다. + + + + +## ChatGPT에서 별도로 확인할 조건 + +2026-09-19에 확인한 OpenAI의 [개발자 모드 문서](https://developers.openai.com/api/docs/guides/developer-mode#how-to-use)는 streaming HTTP와 OAuth 등을 사용하는 원격 MCP 앱을 설명합니다. 그러나 이것이 CodeSpace의 정적 Bearer 설정을 ChatGPT 인증 흐름으로 사용할 수 있다는 근거는 아닙니다. + +CodeSpace에는 OAuth 인증 서버가 없습니다. HTTP Host도 별도의 공개 호스트 설정이 아닌 바인드 설정을 바탕으로 검증합니다. 공개 HTTPS 접근, Host 처리, 인증, 도구 조회, 안전한 시험 호출을 각각 확인해야 합니다. 인증 방식이 맞지 않는다고 쓰기 가능한 작업 공간을 인증 없이 공개해서는 안 됩니다. + +## 검증 상태 + +| 확인 항목 | 근거 또는 남은 작업 | | --- | --- | -| Local stdio `tools/list` + `workspace_info` | `cargo test -p codespace-server` | -| Local Streamable HTTP `tools/list` + `workspace_info` | `cargo test -p codespace-server` | -| ChatGPT Custom Connector against a public HTTPS URL | **Not verified.** This session has no ChatGPT account UI to complete a Custom Connector. Bearer acceptance by ChatGPT is unknown. | -| Public tunnel (ngrok/cloudflare) | **Not verified** | +| 로컬 stdio 초기화와 도구 호출 | 저장소의 전송·프로토콜 테스트 | +| 로컬 Streamable HTTP 초기화와 도구 호출 | 저장소의 HTTP·프로토콜 테스트 | +| 실제 ChatGPT 계정 연결 | 미검증. 사용할 계정과 배포 환경에서 확인 필요 | +| 공개 HTTPS·프록시 구성 | 루프백 테스트로는 확인되지 않음 | +| ChatGPT의 정적 Bearer 수용 여부 | 확인되지 않았으므로 지원한다고 가정하지 않기 | + +저장소가 테스트하는 버전은 [프로토콜 호환성](protocol-compatibility.md)에 있습니다. 그 테스트로 ChatGPT의 협상 버전이나 선택적 MCP 기능의 필요 여부를 추정하지 마세요. + + + -## 비밀 +## 인증 정보 -로그, 이슈 댓글, 도구 오류 텍스트에 `CODESPACE_HTTP_TOKEN`을 넣지 마세요. -HTTP 401 본문은 `{ "error": "unauthorized" }`뿐입니다. +토큰을 이슈에 공유하는 명령, 로그, 커밋된 예시에 넣지 마세요. HTTP 인증 실패 응답에는 `{"error":"unauthorized"}`만 포함됩니다. 계정 비밀과 공개 범위는 운영자가 관리하며 이 가이드가 자동으로 설정하지 않습니다. diff --git a/docs/ko/codex-reuse.md b/docs/ko/codex-reuse.md index 601024c..8fe38ee 100644 --- a/docs/ko/codex-reuse.md +++ b/docs/ko/codex-reuse.md @@ -1,416 +1,83 @@ -# Codex 재사용: 제품과 프리미티브 + + -[English](../codex-reuse.md) | [한국어](codex-reuse.md) - -CodeSpace는 Codex 에이전트를 넣지 **않습니다**. 모든 실행 메커니즘을 -처음부터 재구현하지도 **않습니다**. - -> CodeSpace는 Codex 실행 코드를 피하지 않습니다. Codex 에이전트/제품 -> 소유권이 CodeSpace 핵심에 들어오는 것을 막습니다. 의미와 인가는 여기에 -> 남고, 저수준 실행은 핀된 Codex 서브그래프에서 옵니다. - -요점은 “코드를 덜 쓰는 것”이 아닙니다. 핀 범프가 PTY, 샌드박스, -hardening, 경로 버그 수정을 물려받아야 합니다. 그다음 CodeSpace는 MCP, -인가, 작업 신원, Runner 계약에 힘을 씁니다. - -이 프로세스는 실행 전용입니다. 모델 없음, Responses API 없음 -([execution-substrate.md](execution-substrate.md)). App Server -**프로토콜**은 MCP 번역 대상이 아닙니다. `command/exec` **형태** -(독립 argv, 핸들, 이후 TTY)는 Runner DTO에 올 수 있습니다. -`codex-exec`, `codex-core`, App Server는 금지된 채로 남습니다. - -```text -WHO MAY → CodeSpace Gateway - MCP contract, workspace, profile meaning, - operation/idempotency, audit, Runner trait - -HOW SAFE → pinned Codex execution subgraph - patch engine, PTY, spawn/reap, Landlock/seccomp, - process hardening, filesystem mechanics, - network enforcement -``` - -서브그래프를 재사용해도 Codex가 인가자가 되지는 **않습니다**. -게이트웨이가 여전히 허용하고, 어댑터가 여전히 실행합니다. - -```text -ChatGPT / Cursor / other MCP host - │ MCP - ▼ -CodeSpace Core ← only authorization authority - ├─ MCP / workspace / profile meaning - ├─ operation_key / operation_id / persist - └─ Runner contract (CodeSpace DTOs; no Codex types) - │ - ▼ - isolated adapter workspace - │ crates/patch (codespace-patch) - │ crates/codex-runtime (codespace-codex-runtime) - │ process-hardening + UDS worker; opt-in - │ crates/pty (codespace-pty) - │ interactive spawn; runner API에 Codex 타입 없음 - │ crates/file-system (codespace-fs) - │ no-follow I/O + 제한된 walk; PathSandbox가 인가, I/O 안전은 어댑터 - │ crates/linux-sandbox-protocol (serde 핸드셰이크; SANDBOX_HELPER_PROTOCOL=1) - │ crates/linux-sandbox (codespace-linux-sandbox 바이너리) - │ 프로세스 경계: prepare / opaque plan / run exec; Restricted 네트워크 hard deny - ▼ - Codex execution subgraph (pinned) → OS -``` - -Codex는 어댑터 뒤의 **구현** 의존성이며 Gateway의 아키텍처 의존성이 -아닙니다. `InProcessRunner`, `UdsRunner`, 이후 원격 러너, 또는 -다른 샌드박스 백엔드는 Codex 타입이 어댑터를 떠나지 않으면 바뀔 수 -있습니다. - -## 재사용 단위는 서브그래프 - -“`codex-apply-patch`만큼 좁아야 한다”를 요구하지 마세요. 물으세요. - -1. 이 서브그래프가 **응집력 있는 실행**인가? -2. **에이전트 / 모델 / 제품** 타입이 Runner 경계를 넘는가? -3. **Gateway 허용을 우회**할 수 있는가? - -넓은 Cargo 그래프 자체는 거절이 아닙니다. PTY fd 처리, 시그널 레이스, -Landlock, 마운트 탈출, 또는 프로세스 hardening을 재구현하면 Codex 버그 -수정이 손으로만 옵니다. 선호: - -```text -Codex bugfix → candidate pin → adapter compile / security / parity → promotion -``` - -그것은 **“`main`을 추적”이 아닙니다.** 릴리스 수락은 -[upstream-update.md](upstream-update.md)에 남습니다. 핀 범프에서 -hardening / PTY / sandbox / filesystem / network 크레이트의 diff는 -실행/보안 변경 로그이지 조용한 의존성 범프가 아닙니다. - -컨테이너 격리와 호스트 샌드박스는 **대체물이 아닙니다**. 컨테이너에 -no-new-privs / seccomp / Landlock / 네트워크 한도를 더하는 것은 -심층 방어입니다. 이후 Environment(로컬 컨테이너, 원격 Linux, 베어 -Linux)는 같은 Linux 샌드박스 서브그래프를 공유할 수 있습니다. - -## 핵심 대 어댑터 - -```text -CodeSpace core - crates/domain, policy, store, server, runner - ────────────────────────────────────────── - NO Codex types (including codex-protocol) - NO Codex crate path dependency - - -isolated adapter (crates/patch today; -crates/codex-runtime today; crates/pty today; -crates/file-system today; crates/linux-sandbox today, -binary-only) - ────────────────────────────────────────── - approved execution subgraph allowed - including transitive codex-protocol - -crates/linux-sandbox-protocol (root workspace) - ────────────────────────────────────────── - serde / serde_json only; no Codex types - SANDBOX_HELPER_PROTOCOL = 1 (not UDS / WIRE_PROTOCOL) - - -adapter boundary - ────────────────────────────────────────── - CodeSpace DTO ↔ Codex DTO -``` - -공개 MCP는 `workspace_id` + 상대 경로로 남습니다. 내부에서 어댑터는 -해석할 수 있습니다. - -```text -MCP virtual path - ↓ -CodeSpace WorkspaceResolver / path scope - ↓ -Codex AbsolutePath / PathUri (adapter only) - ↓ -Runner helper -``` - -## 정책 대 메커니즘 - -| CodeSpace가 소유 (정책) | Codex를 선호 (메커니즘) | -| --- | --- | -| workspace / profile allow | PTY, UDS transport primitive | -| path permission meaning | filesystem walk / symlink mechanics | -| network permission meaning | seccomp / Landlock / hardening | -| operation approval | network enforcement (when needed) | -| Runner RPC contract | shell parse / argv construction | - -`codex-execpolicy`는 어댑터에서 파싱하거나 분류할 수 있습니다. 최종 -`allow(command)`가 **아닙니다**. - -## `apply_patch` 패턴 (격리이지 크레이트 너비가 아님) - -엔진을 재사용하세요. 그 주변 서비스는 소유하세요. 이후 런타임 어댑터도 -같은 패턴입니다. - -- 핀은 [upstream-lock.md](upstream-lock.md)에 남습니다 - (`6b9826e3aa83b1a5947db50f4332cb9c65f1b340`). -- **격리된** Cargo 워크스페이스에서의 경로 의존성이며 저장소 루트가 - 아닙니다. 오늘: `crates/patch`, `crates/codex-runtime` - (`codespace-codex-runtime`), `crates/pty` (`codespace-pty`), - `crates/file-system` (`codespace-fs`), `crates/linux-sandbox` - (`codespace-linux-sandbox` 바이너리). 러너는 - `crates/linux-sandbox-protocol`(serde만, 루트 워크스페이스 멤버)로 - 그 헬퍼와 대화합니다. -- NOTICE + Apache-2.0 귀속. -- 제품 정책은 서브그래프 앞과 뒤에 남습니다. -- Codex 워크스페이스에서 크레이트를 파일 복사하지 마세요. - -```text -Gateway → Runner trait → UdsRunner (opt-in) - → codespace-codex-runtime helper → Codex execution crates -``` - -루트 워크스페이스에 Codex 경로 의존성이 생기면 안 됩니다. - -정책 스캔(`scripts/check-no-model-deps.sh`)은 Codex 서브모듈 **없이** -돌아가는 싼 병렬 CI job입니다. 비용은 이 grep이 아니라 서브모듈 -checkout과 cargo가 지배합니다. - -| 구역 | 경로 | 비용 | 때 | -| --- | --- | --- | --- | -| core manifests | root + `crates/{domain,policy,runner,store,server}/Cargo.toml` | tiny | crate in the update range | -| core sources | those crates’ trees | low | same | -| server tests | `tests/` | low | `crates/server` or `tests/` changed | -| adapter manifests | `crates/patch/Cargo.toml`; `crates/codex-runtime`; `crates/pty`; `crates/file-system`; `crates/linux-sandbox` | tiny | adapter in the update range; allowlist only | -| protocol crate | `crates/linux-sandbox-protocol` | tiny | `codex-` 키 없음; runner는 helper 라이브러리 path dep 금지 | -| upstream | `third_party/codex` | huge / false positives | never | +# CodeSpace의 Codex 재사용 범위 -갱신 범위는 `SCAN_BASE`(PR base / 이전 `main`)입니다. 범위를 모르면 -**모든** 핵심 크레이트와 어댑터 매니페스트를 스캔합니다(diff 실패로 -건너뛰지 않음). 문서만 바뀌면 이 job은 exit 0으로 건너뛰고, rust job은 -여전히 실행됩니다. - -핵심 매니페스트는 어떤 `codex-` 의존성 키도 금지합니다. 어댑터 -매니페스트는 승인된 서브그래프만 허용합니다(오늘 `crates/patch`: -`codex-apply-patch`, apply-patch 워크스페이스 그래프로서 -`codex-exec-server`, `codex-utils-path-uri`, `codex-process-hardening`; -`crates/codex-runtime`: `codex-process-hardening`, `codex-uds`; -`crates/pty`: `codex-utils-pty`; `crates/file-system`: -`codex-file-system`, `codex-exec-server`, `codex-utils-path-uri`; -`crates/linux-sandbox`: `codex-linux-sandbox`, `codex-sandboxing`, -`codex-protocol`, `codex-utils-path-uri`). 소스는 에이전트/모델 -패턴을 유지합니다(`api.openai.com`, Responses, `codex-login`, -`codex-core`, `codex-app-server`, `async-openai`). 크레이트 이름을 -언급하는 주석은 cargo 의존성이 아닙니다. +[English](../codex-reuse.md) | [한국어](codex-reuse.md) -linux-sandbox **helper lock**은 Rama **0.3.0-alpha.4** leaf -크레이트(`rama-error`, `rama-macros`, `rama-utils`)를 resolver -가드로 고정합니다. **file-system lock**(`crates/file-system`)도 같은 -leaf를 고정합니다. root가 `codespace-fs` → `codex-exec-server` / -`codex-protocol`로 Rama를 보기 때문입니다. Codex 핀 `6b9826e`는 그 -train으로 검증되어 있습니다. 새로 resolve하면 `rama-core`는 -alpha.4인데 leaf만 stable `0.3.0`이 될 수 있습니다. 러너는 helper -크레이트를 path 의존하지 **않으므로** helper 가드가 sandbox를 통해 -root lock에 들어오지 않습니다. helper와 file-system CI -`cargo clippy` / `cargo test`는 `--locked`입니다. rust job은 -`cargo tree -p codespace-runner`에서 helper-package -edge(`codespace-linux-sandbox`, `codex-linux-sandbox`)를 검사합니다. -`codex-sandboxing` / `landlock` / `seccompiler`는 `codespace-fs` → -`codex-protocol`로 남을 수 있으며, 그건 sandbox helper 그래프가 -아닙니다. +CodeSpace는 특정 버전에 고정한 Codex 소스의 실행 라이브러리를 선택적으로 사용합니다. 계획과 코드 생성은 외부 에이전트가 담당합니다. MCP, 작업 공간 권한, 작업 식별자, 프로세스 관리는 CodeSpace가 담당합니다. -독립 `apply_patch` 바이너리를 보안 경계로 감싸지 **마세요**. Codex App -Server를 내부 백엔드로 감싸지 **마세요**. + + + + -## 감독 코드가 아직 있는 이유 +## 실제 연결된 구성 요소 -`process_id`, stdin, terminate, timeout이 모이는 이유는 요청 수명이 -프로세스 수명이 아니기 때문입니다. 프로세스 내부 감독이 **기본**입니다. -선택적 `UdsRunner`도 그 감독을 `codespace-codex-runtime` 안에서 -돌립니다. `operation_key` / `operation_status`는 잃어버린 **원격 MCP -변경 RPC**를 복구하며, Codex 스레드를 복구하지 않습니다. +| 어댑터 | Codex 구성 요소 | 현재 역할 | +| --- | --- | --- | +| `crates/patch` | `codex-apply-patch`, `codex-exec-server::LOCAL_FS`, 경로 도구, 프로세스 보호 | `codespace-patch` 안에서 V4A 파싱·적용 | +| `crates/codex-runtime` | `codex-process-hardening`, `codex-uds` | 선택적 worker의 보호 설정과 Unix 소켓 | +| `crates/pty` | `codex-utils-pty` | `tty: true`의 터미널 실행 | +| `crates/file-system` | `codex-file-system`, `LOCAL_FS`, 경로 도구 | 심볼릭 링크를 따라가지 않는 Runner 파일 I/O와 범위가 제한된 탐색 | +| `crates/linux-sandbox` | `codex-linux-sandbox`, `codex-sandboxing`, `codex-protocol`, `codex-network-proxy` | 실행 파일 전용 명령 샌드박스 도우미와 enabled 네트워크 프록시 | -spawn+PTY를 얻으려고 `codex-core` / `codex-exec` / App Server를 끌어오면 -login, models, plugins, rollout도 따라옵니다. 그 폭발 반경은 여전히 -거절입니다. +의존성이 존재한다고 그 구성 요소의 서비스 전체가 실행되는 것은 아닙니다. 예를 들어 파일·패치 어댑터는 `codex-exec-server`의 `LOCAL_FS`를 사용하지만 그 서버를 일반 명령 실행 백엔드로 사용하지는 않습니다. Linux 도우미가 프록시와 샌드박스 변환을 관리하며 공개 타입은 CodeSpace 타입으로 유지합니다. -## 단계적 가져오기 (그 WP가 생길 때) + + + + + + -문서화된 순서입니다. **이 WP에서 코드로 가져옴:** process-hardening, UDS, -PTY(`crates/pty` → `codex-utils-pty`), filesystem(`crates/file-system` -→ `LOCAL_FS` / `ExecutorFileSystem`), linux-sandbox -(`crates/linux-sandbox` 바이너리 → prepare / opaque plan / `run --plan` -Restricted `exec` / Enabled 관리 프록시). **가져옴:** -network(`Enabled` + proxy). +## 어댑터 경계 ```text -process-hardening → PTY → UDS / path → filesystem → linux-sandbox → network +에이전트 → CodeSpace MCP·정책·저장소 → Runner 계약 + → 어댑터 → Codex 실행 라이브러리 → OS ``` -복잡도와 잠금은 그 순서로 커집니다. 핀은 가져가는 모든 서브시스템을 -한 번에 정의합니다. - -## 핀 `6b9826e`의 후보 - -Codex `main`이 아니라 핀의 `Cargo.toml` 파일로 판단합니다. - -### 지금 재사용 (코드에서) - -**`codex-apply-patch`** via `crates/patch`. 파싱, 헝크 검증, 적용, -패리티 부분집합. - -**`codex-process-hardening`** via `codespace-patch`와 -`codespace-codex-runtime` `pre_main_hardening()`. 워커/헬퍼 **프로세스** -강화이지 command sandbox가 아닙니다. `main` 첫 줄로 유지하고, 의존성 -폭이 커지지 않는 한 `ctor`는 넣지 않습니다. - -**`codex-uds`** via `codespace-codex-runtime` bind. RPC는 CodeSpace. - -**`codex-utils-pty`** via `crates/pty` (`codespace-pty`). -Unix: `portable-pty`, `tokio`, `libc`. 기본 크기 24x80. 연결해도 PTY -MCP 도구가 추가되지는 **않습니다**. `exec_command`에 선택적 `tty`(기본 -false). 게이트웨이가 여전히 `process_id`를 발급합니다. Resize는 -Runner/MCP 표면에 두지 않습니다(P1). - -**`codex-file-system`** via `crates/file-system` (`codespace-fs`). -제한된 walk, `LOCAL_FS`를 통한 no-follow I/O(`sandbox: None`). -공개 타입은 CodeSpace(`Path` / bytes / walk 결과 / `FsError`)만. -`PathSandbox`는 **인가자**(논리 워크스페이스 선택)로 남습니다. I/O -안전 경계가 아닙니다. 살아 있는 프로세스가 사전 검사와 경쟁할 수 -있습니다. `codespace-fs`가 레이스에 강한 no-follow -open/read/write/remove/walk와 typed error(`SymlinkRejected`, -`NotRegularFile`)를 소유합니다. MCP `read` / `find`는 워크스페이스 -상대로 남습니다. - -**`codex-linux-sandbox`** via `crates/linux-sandbox` -(`codespace-linux-sandbox` 바이너리). -([`codex-rs/linux-sandbox/Cargo.toml`](../../third_party/codex/codex-rs/linux-sandbox/Cargo.toml)) - -라이브러리 어댑터가 아니라 **프로세스 경계**입니다. 러너는 -`SandboxPrepareRequest` JSON(`SANDBOX_HELPER_PROTOCOL = 1`)을 -`prepare`에 보내고 plan **경로만** 받은 뒤 managed `run --plan`을 -spawn합니다. Restricted는 0600 plan을 unlink하고 같은 PID에서 Codex argv로 -`exec`합니다. Enabled는 헬퍼가 `NetworkProxy`를 띄우고 -`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`를 채운 뒤 Codex argv 자식을 -spawn+wait합니다(`--allow-network-for-proxy`; `--proxy-route-spec`은 -plan에 넣지 않고 Codex outer가 런타임에 붙입니다). `WIRE_PROTOCOL`은 -`3`으로 남습니다. Restricted 네트워크는 `--unshare-net`과 Restricted -seccomp입니다. Enabled는 Codex FullAccess 호스트 네트워크가 아니라 -격리 netns + 그 관리 프록시입니다. 런타임 의존성에는 `codex-core`가 -없습니다. **dev-dependencies에는 있습니다** — 어댑터 시험이 그 -그래프를 제품 바이너리로 끌어오면 안 됩니다. -`codex_protocol::PermissionProfile`은 헬퍼 안에 남습니다. -`codespace-runner`는 `codespace-linux-sandbox-protocol`만 의존합니다. - -**헬퍼에서 직접:** `codex-network-proxy`(Enabled `run --plan`의 -`NetworkProxy` 수명). **전이(어댑터에서 허용):** `codex-sandboxing`, -`codex-protocol`. 허용 엔진이 아닙니다. 루트 워크스페이스 의존성이 -아닙니다. - -### 재사용 선호 (그 WP가 올 때) - -**`codex-uds`** (이미 `codespace-codex-runtime`에 있음) -([`codex-rs/uds/Cargo.toml`](../../third_party/codex/codex-rs/uds/Cargo.toml)) - -Unix: Tokio `fs` / `net` / `rt`. 선택적 Runner Unix 소켓 워커의 소켓 -프리미티브입니다. **RPC 프로토콜은 CodeSpace 소유로 남습니다.** - -**`codex-utils-absolute-path` / `codex-utils-path-uri`** - -작은 경로/URI 층(`dirs`, `dunce`, URL). 패치가 이미 필요합니다. MCP는 -여전히 워크스페이스 상대 경로만 노출합니다. - -**`codex-file-search`** -([`codex-rs/file-search/Cargo.toml`](../../third_party/codex/codex-rs/file-search/Cargo.toml)) - -`ignore`, `nucleo`, Tokio. `codex-core` 없음. MCP는 -`find(query, workspace_id)`로 남고, 엔진은 어댑터 뒤로 옮길 수 있습니다. - -### 조건부 / 적극 평가 - -**`codex-shell-command`** - -Tree-sitter Bash/PowerShell, shlex, `which`. 파싱 / 인용 / 실행 파일 -해석만. 허용 엔진이 아닙니다. - -### 내부 프로토콜 후보 - -**`codex-exec-server-protocol`** -([`codex-rs/exec-server-protocol/Cargo.toml`](../../third_party/codex/codex-rs/exec-server-protocol/Cargo.toml)) - -file-system, network-proxy, protocol, shell-command, path-uri. 이후 -워커 DTO / 어댑터 기반. MCP나 `crates/domain` 타입이 **아닙니다**. - -### 격리 층 전이: `codex-protocol` - -무거움: execpolicy, http-client, network-proxy, extension items, -Linux에서 Landlock/seccompiler. **핵심에서 금지.** 어댑터에서는 -허용합니다. 금지하면 file-system, sandbox, shell-command를 다시 짜야 -합니다. `codex_protocol::PermissionProfile`은 MCP나 `crates/domain`에 -나타나면 안 됩니다. - -### 실험적 백엔드 (지금은 아님) - -**`codex-exec-server`** -([`codex-rs/exec-server/Cargo.toml`](../../third_party/codex/codex-rs/exec-server/Cargo.toml)) - -HTTP/WS plus `codex-api`, `codex-config`, OTel, protocol, sandboxing, -PTY. 오늘의 Runner 백엔드로는 너무 무겁습니다. 영구 거절은 아닙니다. -나중에 UdsRunner + 저수준 크레이트 대 Gateway 어댑터 → -exec-server를 비교하세요. 컴파일 그래프와 업그레이드 비용을 재세요. - -### 이후 Environment (P0 아님) - -**`codex-git-utils` / `codex-worktree`** — Environment 프로비저닝이 -필요하면 격리된 checkout / worktree 수명주기. file-system, protocol, -PTY, `gix`를 끌어옵니다. 그 WP까지 빼 두세요. - -### 코드는 허용, 권한은 금지 - -**`codex-execpolicy`** — Starlark 접두 규칙. 어댑터의 분류/파싱은 -괜찮습니다. 최종 허용은 Gateway에 남습니다. - -### 거절 - -**`codex-exec`** — App Server 클라이언트, `codex-core`, login, config, -rollout, history. 제품 exec 흐름이지 `spawn`이 아닙니다. - -**`codex-core`** — 에이전트 루프, 도구, 세션. - -**App Server embed** (`ChatGPT → MCP adapter → Codex App Server`) — -에이전트 인프라를 다시 가져오고 인가를 나눕니다. - -**login / model / Responses** — 실행 전용 위반. - -**Codex 세션 `permissionProfile` / 사용자 샌드박스 설정을 허용으로** — -두 번째 인가자. - -## CodeSpace에 남는 것 - -- MCP 도구 스키마와 도메인 타입(runner/domain에 `rmcp` 없음. 핵심에 - Codex 타입 없음). -- 워크스페이스 레지스트리, 프로필의 **의미**, 경로 정책. -- 쓰기 잠금, 셸 점유, `WORKSPACE_BUSY`(스케줄러 WP까지). -- `operation_key` 재실행, `operation_id`, `operation_status`. -- 호스트/프로세스 내부 프로세스 감독이 기본. UDS 워커는 선택적. -- 컨테이너 수명주기와 워크스페이스 바인드 마운트 **정책**. -- 격리된 어댑터 워크스페이스(`crates/patch`, - `crates/codex-runtime` / `codespace-codex-runtime`, `crates/pty` / - `codespace-pty`, `crates/file-system` / `codespace-fs`, - `crates/linux-sandbox` / `codespace-linux-sandbox` 바이너리). - 핸드셰이크 타입은 `crates/linux-sandbox-protocol`에 있습니다. - -## 다음 구현 WP - -다음 **코드** 작업 패키지는 기존 `Runner` 트레이트 뒤의 남은 실행 -서브그래프(network: `Enabled` + proxy)입니다. -`apply_patch`를 게이트웨이가 구동하는 여러 RPC로 쪼개면 안 됩니다. - -기본으로 자체 PTY / Landlock / seccomp 스택을 두지 마세요. -위 표 이후 격리된 워크스페이스를 통해 실행 서브그래프를 가져오세요. -핀 범프는 의도적 릴리스입니다 -([upstream-update.md](upstream-update.md)): 지금은 SHA + 패치 패리티. -그 크레이트를 가져가면 런타임 어댑터 빌드와 PTY / sandbox / process -회귀를 더합니다. - -남은 도메인 확장(스케줄러 큐, 승인 도구)은 -[execution-substrate.md](execution-substrate.md)에서 순서를 정합니다. -실제 MCP 도구 이름은 그대로입니다. `exec_command`에 선택적 `tty`가 -생겼습니다. +핵심 crate에는 직접적인 Codex 의존성이 없습니다. 어댑터는 고정된 업스트림의 workspace 의존성을 수용하기 위해 별도의 Cargo workspace로 구성합니다. 파일 시스템·PTY 어댑터는 Runner의 라이브러리 의존성이며, 패치와 Linux 샌드박스는 도우미 프로세스를 사용합니다. Cargo workspace를 분리하는 것만으로 프로세스나 보안 경계가 생기지는 않습니다. + +Linux 샌드박스 도우미는 실행 파일만 제공합니다. `codespace-linux-sandbox-protocol`에는 CodeSpace가 정의한 핸드셰이크 데이터만 있고 Codex 타입은 없습니다. worker의 UDS 프로토콜 버전 3과 샌드박스 도우미 프로토콜 버전 1은 별개의 계약입니다. + + + + + + + + + + + + +## 권한 결정은 CodeSpace에서 수행 + +게이트웨이 정책은 작업 공간에서 허용할 행동을 결정합니다. Codex 실행 코드는 no-follow 파일 접근, PTY 생성, 샌드박스 설정 같은 기능을 구현합니다. Codex 세션 권한, 로그인, 모델 선택, 에이전트 루프를 가져오는 것은 이 책임 구분을 바꾸는 일이며 현재 제품에 포함되지 않습니다. + +현재 진입점은 `codex-core`, `codex-exec`, Codex App Server를 제품 런타임으로 내장하지 않습니다. 간접 의존성 그래프의 범위와 실제 호출 경로는 별도로 평가합니다. [보안 경계](security-model.md)를 참고하세요. + + + + + + + + + + + + + + + + + + +## 업데이트와 향후 검토 + +재사용하는 구성 요소는 현재 모두 [같은 고정 버전](upstream-lock.md)에서 가져옵니다. 업스트림 수정은 버전 갱신과 검증을 거쳐야 반영되며 자동으로 들어오지 않습니다. [업데이트 검사](upstream-update.md)는 패치뿐 아니라 연결된 모든 어댑터를 포함합니다. + +`codex-file-search`, 셸 명령 파싱, worktree 준비, 범용 `codex-exec-server` 백엔드는 아직 연결되지 않은 후보입니다. 도입 시 제공하는 실행 기능, 빌드·업데이트 비용, 모델이나 권한 결정 책임이 어댑터 경계를 넘는지를 검토합니다. 사용자에게 영향을 주는 현재 제약은 [Agent Loop 연동](agent-integration.md)에 정리되어 있습니다. diff --git a/docs/ko/documentation.md b/docs/ko/documentation.md index 4465e80..83cb681 100644 --- a/docs/ko/documentation.md +++ b/docs/ko/documentation.md @@ -1,38 +1,43 @@ -# 공개 문서와 문서 사이트 + + + +# 문서 유지보수 [English](../documentation.md) | [한국어](documentation.md) -영문 파일이 편집 원본입니다. 루트 가이드는 대응하는 `.ko.md` 파일을 -사용합니다. `docs/` 아래 가이드는 `docs/ko/`를 사용합니다. 상호 언어 -링크를 유지하세요. 명령, 코드 블록, 식별자, 지원 조건은 각 쌍에서 -같아야 합니다. +공개 사이트와 저장소 안내는 같은 Markdown 원문을 사용합니다. 영문을 편집 기준으로 삼고 각 문서에 검토된 한국어판을 유지합니다. 영문이 불명확하면 먼저 고친 뒤 동작·제약·예시가 같은 자연스러운 한국어로 작성하세요. + +## 내용과 표현 검토 + +현재 코드·설정·테스트와 문서를 대조합니다. 구현된 동작, 설정에 따른 사용 가능 여부, 테스트 범위, 실제 배포 검증을 구분하세요. 내부 구현 이름보다 독자가 할 수 있는 일을 먼저 설명하고 필요한 기술 용어는 처음 등장할 때 풀이합니다. + +설명 없는 작업 번호, 과거 세션의 주장, 반복되는 부정 비교, 관련 없는 저장소 이름을 피합니다. 의존성 출처와 유용한 소스 링크는 유지하세요. 표의 설명과 탐색 문구는 번역하고 API 식별자는 보존합니다. 한국어는 영어 어순을 그대로 옮기지 않고 독립적으로 자연스럽게 읽혀야 합니다. + +각 사실을 자세히 설명하는 기준 페이지를 정하고 다른 곳에서는 연결합니다. 설치 절차는 재현 가능하게 작성하고, 자리표시자와 실행 가능한 예시를 구분하며, 결과가 불확실하면 성공을 암시하지 말고 처리 방법을 설명합니다. 해시를 기록하기 전에 문서 쌍 전체를 다시 읽고 일관성을 확인하세요. + +## 문서 등록과 호환성 + +[레지스트리](../translations.json)는 문서 ID, 경로, 탐색 그룹, 앵커, 검토한 파일 해시를 기록합니다. 루트 안내는 `.ko.md`, 다른 번역은 `docs/ko/`에 둡니다. 대응 문서가 있으면 같은 언어로 연결하세요. + +절 제목을 바꿔도 기존 경로와 앵커는 보존합니다. 이전 앵커를 새 절 가까이에 명시적으로 두세요. 새 관리 문서에는 두 언어와 등록 항목이 필요합니다. 해시는 이후 편집을 감지할 뿐 의미 일치나 편집 검토를 증명하지 않습니다. -[문서 레지스트리](../translations.json)는 안정 ID, 탐색 그룹, 보존 -앵커, 원본/번역 경로, 검토한 파일 해시 양쪽을 기록합니다. 해시는 -드리프트를 감지합니다. 의미 동등이나 사람 승인을 증명하지 않습니다. -쌍을 기록하기 전에 두 문서 전체를 검토하세요. +변경한 문서 쌍을 검토한 뒤 해당 ID를 명시하여 기록합니다. -```sh +```bash +python3 -B scripts/check_docs.py record --id agent-integration python3 -B scripts/check_docs.py -python3 -B scripts/check_docs.py record --id documentation ``` -두 번째 명령은 해당 문서 쌍을 검토한 뒤 편집자가 사용합니다. 검토한 -ID를 명시적으로 고르세요. 유지하는 새 Markdown 문서는 한국어판과 -레지스트리 항목이 필요합니다. CI는 검토 기록을 자동으로 갱신하지 -않습니다. +검사 실패를 없애기 위해 모든 해시를 갱신하지 마세요. 렌더링한 페이지, 복사되는 Markdown, 언어 이동, 기존 링크가 의도한 내용과 일치하는지도 확인합니다. -## 문서 사이트 + + -사이트는 커밋된 [npm lock](../../docs-site/package-lock.json)과 함께 -VitePress 1.6.4, Node 24.21.0, npm 11.19.0을 사용합니다. 사이트 구현은 -MIT이며 docs-actions에서 복사해 여기서 맞췄습니다. -[사이트 출처](../../docs-site/PROVENANCE.json)를 보세요. 프로젝트 문서는 -Apache-2.0입니다. +## 빌드와 미리 보기 -Vite 개발 서버를 시작하지 마세요. 저장소 루트에서: +Node 24.21.0, npm 11.19.0, Python 3.11 이상을 사용합니다. CI는 Python 3.14를 사용합니다. Python 실행 파일 이름이 다르면 `DOCS_PYTHON`을 설정하세요. 사이트는 커밋된 VitePress 잠금 파일을 사용하며 문구 수정에 의존성 업데이트를 섞지 않습니다. -```sh +```bash npm ci --prefix docs-site --ignore-scripts npm test --prefix docs-site npm run build --prefix docs-site @@ -40,27 +45,15 @@ python3 -B docs-site/scripts/site.py check python3 -B docs-site/scripts/site.py preview ``` -미리보기는 `http://127.0.0.1:43141/CodeSpace/`에서 수신합니다. 수정 후 -다시 빌드하세요. 미리보기에는 핫 모듈 교체가 없습니다. 영어는 사이트 -루트이고 한국어는 `/ko/` 아래입니다. 유지 문서는 `/guide/`와 -`/ko/guide/`에 있습니다. 페이지 복사는 현재 언어의 유지 Markdown을 -복사합니다. 로컬 검색은 브라우저에 남습니다. +미리 보기 주소는 `http://127.0.0.1:43141/CodeSpace/`입니다. 수정 후에는 미리 보기를 중지하고 다시 빌드한 뒤 재시작하세요. 시작할 때 읽은 manifest와 일치하는 파일만 제공하기 때문입니다. Vite 개발 서버 대신 검증된 정적 미리 보기를 사용합니다. 영문은 루트, 한국어는 `/ko/`이며 안내 문서는 `/guide/`, `/ko/guide/`에 있습니다. 페이지 복사는 현재 언어의 Markdown 원문을 사용합니다. 검색은 브라우저 안에서 수행합니다. -Python 실행 파일 이름이 다르면 `DOCS_PYTHON`을 설정하세요. Python 3.11 -이상을 사용합니다. CI는 Python 3.14를 고릅니다. +두 테마에서 데스크톱과 좁은 화면을 확인하고 긴 코드·표도 점검하세요. 탐색, 언어 전환, 검색, 복사를 실제로 사용해 봅니다. 레지스트리와 빌드 검사 통과만으로 가독성이나 정확성이 보장되지는 않습니다. -## 게시 + + -끌어오기 요청은 읽기 전용 문서 빌드를 실행하고 검토 산출물을 보관합니다. -배포하지 않습니다. `main` push 또는 수동 `main` 실행은 같은 검증된 -디렉터리를 패키징하고 핀된 docs-actions 재사용 워크플로를 호출합니다. -그 배포 job에만 `pages: write`와 `id-token: write`를 주세요. -`secrets: inherit`를 쓰지 마세요. +## 게시와 출처 -핀은 [`.github/docs-pages-deploy.lock.json`](../../.github/docs-pages-deploy.lock.json)입니다. -새 SHA는 해당 커밋, 라이선스 범위, 성공한 중앙 CI를 검토한 뒤에만 -채택하세요. +PR은 리뷰용 산출물을 빌드하며 배포하지 않습니다. main push 또는 main의 수동 workflow가 검증한 디렉터리를 GitHub Pages에 게시합니다. 빌드 메타데이터에 소스 커밋과 원문 해시를 기록하지만, 산출물 업로드 성공이 실제 게시 완료의 증거는 아닙니다. -GitHub Pages 소스, `github-pages` 환경, 공개 재사용 워크플로 허용은 -저장소 설정입니다. 이 문서 빌드가 적용하지 않습니다. 성공한 로컬 빌드나 -보관한 산출물은 라이브 사이트가 아닙니다. +사이트 구현은 MIT, 프로젝트 문서는 Apache-2.0을 따릅니다. [사이트 출처](../../docs-site/PROVENANCE.json), 의존성 고지, 라이선스 파일을 보존하세요. 게시 workflow는 [배포 잠금 기록](../../.github/docs-pages-deploy.lock.json)의 검토된 외부 액션을 사용합니다. 이 액션은 별도의 의존성 검토를 거쳐 변경합니다. 저장소의 Pages·환경 설정은 운영자가 관리하며 빌드가 생성하지 않습니다. diff --git a/docs/ko/error-codes.md b/docs/ko/error-codes.md index b50f2dd..9dcd720 100644 --- a/docs/ko/error-codes.md +++ b/docs/ko/error-codes.md @@ -1,100 +1,60 @@ -# 오류 코드와 전송 대 실행 + + + +# 오류와 불확실한 결과 [English](../error-codes.md) | [한국어](error-codes.md) -HTTP/JSON-RPC **요청 id**, `operation_id`, `process_id`는 세 개의 서로 -다른 식별자입니다. 잃어버린 HTTP 응답은 실행 실패가 아닙니다. -클라이언트는 변경 도구를 재실행하는 대신 `operation_status`(W08)를 -호출합니다. +먼저 전송 실패와 도구 결과를 구분하세요. HTTP·연결 오류만으로 요청한 변경이 실행되었는지 알 수 없습니다. 패치·프로세스·작업 관리 상태는 서로 다른 식별자를 사용합니다. -## 전송 실패 (작업 없음) + + -이들은 `operation_id`를 절대 발급하지 않으며 작업으로 저장되면 안 -됩니다. +## 전송 실패 -| 신호 | 의미 | -| --- | --- | -| TCP reset / client disconnect | Wire died. The process or patch may still be running. | -| HTTP 401 / 403 from Bearer middleware | Auth failed **before** any tool handler. | -| HTTP 404 at `/mcp` | Wrong path. | -| HTTP 408 / 502 / 503 / 504 | Transport or proxy. | +핸들러 실행 전 인증이 거부되면 작업 기록을 만들지 않습니다. 반면 연결 끊김이나 프록시 오류는 실행 요청 전달 후에도 발생할 수 있으므로 작업이 없다는 증거가 아닙니다. 도메인 분류 함수는 HTTP 401/403, 404, 408, 429, 502/503/504를 전송 실패로 분류합니다. 실제 요청 단계와 기록된 작업을 확인해 가능한 복구를 수행하세요. -`codespace_domain::classify_http_status`는 이를 -`FailureClass::Transport`로 매핑합니다. `TRANSPORT_FAILURE_IS_NOT_OPERATION`은 -참입니다. + + -## 실행 오류 코드 (도구 결과) +## 도구 오류 코드 -JSON에서 `SCREAMING_SNAKE_CASE`로 직렬화됩니다. +오류는 대문자 식별자와 메시지로 전달합니다. 실패 전에 패치 기록을 만들었다면 `operation_id`가 포함될 수 있습니다. 그 전에 발생한 권한·점유·키 충돌 오류에는 작업 ID가 없습니다. -| 코드 | 때 | +| 코드 | 의미 | | --- | --- | -| `UNAUTHORIZED` | Tool-layer refusal after a valid transport (not Bearer 401) | -| `WORKSPACE_NOT_FOUND` | Unknown `workspace_id` (W04) | -| `WORKSPACE_BUSY` | Write lock held by a live shell (W08 / W10) | -| `INVALID_PATCH` | 패치 파싱/검증(W06 / W09); after-hash / delete-still-present / 생략된 `after_version`. rollback 파일시스템 I/O는 이 코드가 아님 | -| `INVALID_COMMAND` | Command request is structurally invalid and was rejected before process dispatch | -| `PROCESS_SPAWN_FAILED` | Execution backend confirmed that no managed process was established | -| `PATH_ESCAPE` | workspace/path containment 위반: `../`·절대경로 요청, 또는 `find` walk 결과가 `workspace.root` 밖 | +| `UNAUTHORIZED` | 도구 정책이 행동을 거부함 | +| `WORKSPACE_NOT_FOUND` | 작업 공간 ID가 등록되지 않음 | +| `WORKSPACE_BUSY` | 다른 변경 작업이나 실행 중인 명령이 작업 공간을 점유함 | +| `INVALID_PATCH` | 패치 파싱·사전 검증·결과 검증 실패. 일부 도우미·입력 오류 경로에도 남아 있음 | +| `INVALID_COMMAND` | 잘못된 명령 인자 배열을 실행 전에 거부함 | +| `PROCESS_SPAWN_FAILED` | 관리 프로세스를 시작하지 못한 것으로 백엔드가 확인함 | +| `PATH_ESCAPE` | 요청 경로가 작업 공간 범위를 벗어남 | | `FILE_NOT_FOUND` | 대상 경로가 없음 | -| `PATH_NOT_DIRECTORY` | 디렉터리여야 하는 경로 구성 요소가 일반 파일임(`ENOTDIR`) | -| `FILE_OPERATION_FAILED` | containment는 유지됐지만 filesystem operation 자체가 실패(`find` 루트 canonicalize 포함) | -| `SYMLINK_REJECTED` | 심링크 파일 또는 조상 | -| `SPECIAL_FILE_REJECTED` | Device, socket, fifo | -| `ADD_FILE_EXISTS` | Add File destination already exists | -| `MOVE_DESTINATION_EXISTS` | Move destination already exists | -| `VERSION_CONFLICT` | `expected_versions` mismatch | -| `OPERATION_KEY_CONFLICT` | Same key, different request (W08) | -| `OPERATION_NOT_FOUND` | Unknown `operation_id` / `operation_key`, or `operation_status` did not receive exactly one of them | -| `PROCESS_NOT_FOUND` | Unknown `process_id` | -| `OUTPUT_LIMIT` | Reserved; live `read_process` drops oldest bytes instead of storing unbounded output | -| `TIMEOUT` | Managed process time limit (default 30s; `CODESPACE_PROCESS_TIMEOUT_SECS`) | -| `WORK_NOT_FOUND` | Unknown `work_id` | -| `WORK_CLOSED` | Mutating steer on a closed work | -| `INTENT_NOT_FOUND` | Unknown `intent_id` | -| `INTENT_ALREADY_CLAIMED` | Edit/cancel after the model claimed the item | -| `INTENT_NOT_EDITABLE` | State is not draft/queued | -| `INTENT_REVISION_CONFLICT` | Optimistic `revision` mismatch | -| `QUEUE_NOT_EMPTY` | Reserved; `work_finish` returns `closed: false` instead of this error | - -적용 결과는 `status`를 사용합니다(`applied`, `checked`, `rejected`, -`failed_rolled_back`, `failed_partial`, `unknown`). `checked`는 성공한 -`check_only` 미리보기입니다. `rejected`는 실제 거절입니다. 실패한 적용은 -절대 `applied`를 보고하지 않습니다. `applied`는 디스크 해시가 헬퍼가 -주장한 `after_version`과 일치해야 합니다. 재시작은 미완료 행을 -`unknown`으로 남기고 자동 적용하지 않습니다. - -`exec_command`는 spawn이 일어났을 수 있을 때 성공 결과에 -`dispatch_status=unknown`을 실을 수 있습니다. 그것은 전송 오류 본문이 -아닙니다. 반환된 `process_id`는 그 불확정 시도를 식별합니다. 새 -프로세스를 시작하지 마세요. 백엔드가 도달 가능할 때만 `read_process` / -`terminate_process`를 쓰세요. unknown이 시작되지 않았다는 뜻은 아닙니다. - -`INVALID_PATCH`는 더 이상 exec command 검증, 확인된 process-spawn -실패, rollback 파일시스템 I/O에 쓰이지 않습니다. - -`codespace-fs` `FsError`는 제품 코드와 1:1입니다. -`NotFound` → `FILE_NOT_FOUND`, `NotDirectory` → `PATH_NOT_DIRECTORY`, -일반 `Io` → `FILE_OPERATION_FAILED`, `SymlinkRejected` → -`SYMLINK_REJECTED`, `NotRegularFile` → `SPECIAL_FILE_REJECTED`. -`PATH_ESCAPE`는 workspace/path containment 위반입니다. 범위를 벗어나려는 -요청(`../`, 절대 경로)이거나, walk 결과가 `strip_prefix(workspace.root)`에 -실패한 경우입니다. `FILE_OPERATION_FAILED`는 containment는 유지된 채 -operation 자체가 실패한 것입니다. - -`INVALID_COMMAND`는 구조적으로 잘못된 argv입니다. 게이트웨이는 -`process_id` 발급과 mutation lease 전에 거절합니다. 러너도 같은 검사를 -반복합니다. `PROCESS_SPAWN_FAILED`는 백엔드가 managed process가 -**만들어지지 않았음을 확정**한 것입니다. 게이트웨이는 잡은 lease를 -해제합니다. Linux helper `prepare` / protocol / helper OS spawn 실패가 -이 코드입니다. managed helper spawn이 성공한 뒤 `run --plan` load, -self-exec, inner sandbox 실패는 process exit이며 이 코드가 아닙니다. -`INVALID_COMMAND`와 `PROCESS_SPAWN_FAILED` 모두 -`dispatch_status=unknown`이 아닙니다. - -`begin`이 `operation_id`를 발급한 뒤, 도구 오류는 그 id를 -`ErrorBody.operation_id`에 포함합니다. `begin` 전의 정책 / 잠금 / -키 충돌 거절은 포함하지 않습니다. - -대기 중인 사용자 입력이 있는 `work_finish`는 **전송 실패가 아닙니다**. -`{ "closed": false, "reason": "pending_user_input" }`를 반환합니다. +| `PATH_NOT_DIRECTORY` | 디렉터리여야 하는 경로 구성 요소가 디렉터리가 아님 | +| `FILE_OPERATION_FAILED` | 허용 범위 안에서 파일 시스템 작업 실패 | +| `SYMLINK_REJECTED` | 심볼릭 링크 경로 거부 | +| `SPECIAL_FILE_REJECTED` | 장치·소켓·FIFO 등 일반 파일이 아닌 대상 거부 | +| `ADD_FILE_EXISTS` | 추가할 파일이 이미 존재함 | +| `MOVE_DESTINATION_EXISTS` | 이동 대상이 이미 존재함 | +| `VERSION_CONFLICT` | 현재 내용이 예상 버전과 다름 | +| `OPERATION_KEY_CONFLICT` | 같은 패치 키를 다른 인자에 사용함 | +| `OPERATION_NOT_FOUND` | 조회 ID·키가 없거나 조회 식별자를 정확히 하나 지정하지 않음 | +| `PROCESS_NOT_FOUND` | 프로세스 핸들이 없거나 만료됨. 입력 시 stdin이 이미 닫힌 경우도 포함 | +| `OUTPUT_LIMIT` | 도우미 출력 상한 초과. 프로세스 출력 조회는 오래된 바이트를 버리는 방식 | +| `TIMEOUT` | 관리 명령 또는 도우미의 제한 시간 초과 | +| `WORK_NOT_FOUND` | 논리적 작업을 찾을 수 없음 | +| `WORK_CLOSED` | 열린 작업에만 가능한 동작 | +| `INTENT_NOT_FOUND` | 사용자 지시를 찾을 수 없음 | +| `INTENT_ALREADY_CLAIMED` | 이미 가져간 지시임 | +| `INTENT_NOT_EDITABLE` | 지시 상태가 편집을 허용하지 않음 | +| `INTENT_REVISION_CONFLICT` | 지시 수정 버전이 달라짐 | +| `QUEUE_NOT_EMPTY` | 예약된 코드. 현재 work_finish는 closed:false를 반환함 | + +## 실행 요청과 완료의 구분 + +`dispatch_status: unknown`은 실행 여부가 불확실하다는 정상 형식의 응답이며 프로세스가 시작되지 않았다는 뜻이 아닙니다. 연결 가능하면 반환된 핸들을 사용하고 중복 실행을 피하세요. `confirmed`도 요청 확인을 뜻하며 명령 성공을 뜻하지 않습니다. + +Linux 도우미 준비·프로토콜·시작 오류는 관리 프로세스 생성 전에 발생하므로 `PROCESS_SPAWN_FAILED`를 사용합니다. 도우미가 이미 시작된 뒤 계획 읽기, 내부 샌드박스, 프록시 시작에서 실패하면 프로세스 종료로 처리됩니다. 현재 공개 결과에 종료 코드가 없으므로 출력 스트림 완료만으로 성공을 판단할 수 없습니다. + +패치는 [상태 표와 복구 한계](behavior-differences.md)를 참고하세요. 재시도, 시간 초과, 사라진 핸들, 사용자 지시 완료는 [Agent Loop 연동](agent-integration.md)을 따릅니다. `work_finish`의 `closed: false`, `reason: "pending_user_input"`는 애플리케이션 결과이며 전송 실패가 아닙니다. diff --git a/docs/ko/execution-substrate.md b/docs/ko/execution-substrate.md index cf11260..8a7ad4f 100644 --- a/docs/ko/execution-substrate.md +++ b/docs/ko/execution-substrate.md @@ -1,327 +1,67 @@ -# 실행 기반 + + -[English](../execution-substrate.md) | [한국어](execution-substrate.md) +# 실행 계약 -CodeSpace는 **실행 전용 MCP**입니다. ChatGPT(또는 다른 MCP 호스트)가 -계획하고 코드를 작성합니다. 이 프로세스는 모델을 호출하지 않고, OpenAI -Responses API를 호출하지 않으며, 에이전트 루프를 실행하지 않습니다. +[English](../execution-substrate.md) | [한국어](execution-substrate.md) -```text -ChatGPT - 판단 / 계획 / 코드 생성 - │ MCP tools/call - ▼ -Headless execution MCP - no model, no Responses API, no agent loop - │ - patch / exec / fs / sandbox / permissions / audit - ▼ -Filesystem / OS / container / (later) remote runner -``` +외부 Agent Loop는 계획, 모델 문맥, 완료 판단을 담당합니다. CodeSpace는 정해진 도구 동작을 수행하고 상태를 제공합니다. 실행 기능을 추가하더라도 내부 모델 호출이나 Codex 에이전트 세션이 필수 조건이 되어서는 안 됩니다. -Codex App Server 아이디어는 다음에 **예**라고 답할 때만 가져옵니다. + + + + -> 이것이 실행, 권한, 상태, 또는 관찰을 **모델 없이 결정적으로** -> 제공할 수 있는가? +## 정책과 실행 구현 -프롬프트, 컨텍스트, 턴, 추론, 검토, 또는 모델 카탈로그가 필요하면 -빼 두세요. App Server JSON-RPC를 MCP로 번역하지 마세요. 기반을 추출해 -CodeSpace 도구로 다시 노출하세요. +게이트웨이는 등록된 작업 공간에서 요청한 행동이 허용되는지 결정합니다. Runner 요청은 CodeSpace 타입으로 전달하고, 어댑터가 이를 Codex 실행 타입으로 변환합니다. 핵심 계층의 의존성 선언과 인터페이스에 Codex crate나 타입을 직접 추가하지 않습니다. 다만 어댑터의 간접 의존성이 빌드 그래프에 포함될 수 있습니다. -인가(**누가 해도 되는지**)는 Gateway에 남습니다. 안전한 실행(**얼마나 -안전한지**)은 Runner 뒤의 핀된 Codex **실행 서브그래프**가 공급합니다 -(구현 의존성이지 아키텍처 의존성이 아님). 핵심 크레이트는 -`codex-protocol`을 포함해 Codex 타입을 가져오면 안 됩니다. 어댑터는 -가져도 됩니다. 가져오기/빼기 표: -[codex-reuse.md](codex-reuse.md). 핀: -[upstream-lock.md](upstream-lock.md) (`6b9826e`, `rust-v0.154.0`). -Codex `main` `4701aa4b`를 언급하는 조사 노트는 핀 범프가 **아닙니다**. -의도적인 W13 갱신 뒤에 그래프를 다시 확인하세요. +운영자 레지스트리의 `read-only`와 `workspace-write`를 실제 권한에 대응시키고, 네트워크는 `restricted` 또는 `enabled`로 선택합니다. 내부 정책 타입에는 세밀한 경로 규칙을 표현할 수 있는 glob 필드가 있지만, 이를 외부에서 설정하고 모든 작업에 적용하는 기능은 제공하지 않습니다. 클라이언트 인자로 권한을 높일 수 없습니다. -CI: `policy-scan` job은 서브모듈 **없이** `scripts/check-no-model-deps.sh`를 -`rust`와 병렬로 실행합니다. 핵심 매니페스트는 `codex-*` 의존성을 선언하면 -안 됩니다. 핵심 소스는 에이전트/모델 grep을 유지합니다. 격리된 어댑터 -매니페스트(`crates/patch`, `crates/codex-runtime`, `crates/pty`, -`crates/file-system`, `crates/linux-sandbox`)는 -**허용 목록**을 사용합니다. `crates/linux-sandbox-protocol`은 루트 -워크스페이스 serde 크레이트입니다(`codex-` 키 없음). `third_party/codex` 소스는 절대 스캔하지 -않습니다. `SCAN_BASE`는 트리를 갱신 범위로 제한합니다. 범위를 모르면 -모든 핵심 크레이트와 어댑터 매니페스트를 스캔합니다. Clippy/시험은 -여전히 항상 실행됩니다. rust job은 fmt/clippy **전에** 핀 SHA를 -검사하고(`PIN_ONLY=1`), `cargo tree -p codespace-runner`로 -sandbox-specific 그래프 edge도 검사합니다. + + -## 불변식 +## 실행 환경과 식별자 -| 해야 함 | 하면 안 됨 | +| 개념 | 현재 용도 | | --- | --- | -| MCP tools for read, patch, exec, process, operations | Responses / Chat Completions client | -| Gateway as the only allow path | Codex session `permissionProfile` as allow | -| Workspace-relative MCP paths | Absolute paths on the wire | -| Process handles that outlive an MCP connection | Copy App Server “kill on connection close” | -| Isolated `crates/patch` → `codex-apply-patch` | Embed `codex-app-server` / `codex-exec` / `codex-core` | - -`read-only` / `workspace-write`가 실제 MCP 프로필로 남습니다. 더 풍부한 -파일시스템 glob + 네트워크 축은 `crates/policy`의 `PermissionProfile`에 -있고 그 프로필에서 매핑됩니다. `process_exec`가 Exec 축입니다 -(`read-only`는 거부, `workspace-write`는 허용). 경로 glob은 **표현만** -있고 live enforcement는 기존 coarse `allow(Write|Exec)` + PathSandbox입니다. -Restricted 네트워크는 Linux 헬퍼 probe가 성공하면 OS에서 강제합니다 -(`workspace_info.execution.network.enforcement=enforced`). `Enabled`는 -호스트 FullAccess가 아니라 격리 netns + 헬퍼 소유 관리 프록시입니다. -헬퍼가 없으면 Enabled는 `PROCESS_SPAWN_FAILED`이며 조용한 허용이 -아닙니다. 운영자 워크스페이스 JSON `network`는 `restricted`(기본) 또는 -`enabled`입니다. MCP 프로필은 `read-only` / `workspace-write`로 남고 -`{ "network": true }`는 부여가 아닙니다. 축이 허용을 올리지는 않습니다. -Codex 사용자 설정을 가져오는 것이 아닙니다. - -## 네 축 (목표 도메인) - -이 모두가 오늘 MCP 필드는 아닙니다. **실제 도구에 `environment_id`를 -추가하지 마세요.** 운영자 JSON은 환경을 등록할 수 있습니다. 생략하면 -암시적 로컬 호스트입니다. 알 수 없는 environment id는 설정 로드에 -실패합니다. `linux-container`는 로드되지만 exec/patch는 닫힌 실패입니다. - -```text -Environment where command and filesystem ops run -Workspace which tree inside that environment is in scope -PermissionProfile what that pair may do (gateway-owned) -Operation this mutating RPC (id, key, persist, audit) - ↓ -Process / Patch / FS -``` - -- **Environment**는 에이전트가 아닙니다. 로컬 호스트, Linux 컨테이너, - 또는 이후 원격 러너가 환경입니다. 등록은 제어 평면 / 운영자 동작입니다. - 모델이 `execServerUrl`을 공급하면 안 됩니다. -- **Workspace**는 MCP의 선택자로 남습니다(`workspace_id` + 상대 경로). - 내부에서 러너는 절대 경로로 해석할 수 있습니다. -- **PermissionProfile** 형태(경로, glob, 또는 특수 루트에 대한 Read / - Write / Deny, `process_exec`, 별도 네트워크 축)는 App Server를 따를 - 수 있습니다. glob은 표현만 있고 live enforce 하지 않습니다. - **부여하는 엔진**은 CodeSpace 정책입니다. -- **Operation**은 이미 `operation_id` / `operation_key` / - `operation_status`입니다. Diff/감사 원장은 P1이며 대화 이력이 - 아닙니다. - -```text -MCP virtual path - ↓ -WorkspaceResolver / CodeSpace path scope - ↓ -absolute path (internal; later Codex AbsolutePath / PathUri in the adapter) - ↓ -Runner / patch helper -``` - -## PathSandbox와 codespace-fs - -`PathSandbox`는 논리 인가와 워크스페이스 선택입니다(상대 경로, 루트 -안, `..` 거절, leaf/조상 심링크와 특수 파일 거절). 사전 -`symlink_metadata` 검사는 **I/O 안전 경계가 아닙니다**. - -공통 I/O primitive는 `follow_symlinks: false`인 Codex `LOCAL_FS`입니다. -`codespace-fs`는 read, find, version, mkdir, chmod, remove, rollback용 -Runner 어댑터입니다. `apply_patch` mutation은 `crates/patch` → -`apply_patch_with_options`로 같은 `LOCAL_FS` pin을 탑니다 -(`sandbox: None`). 헬퍼 프리플라이트와 사후 hash는 아직 `std::fs`를 -쓸 수 있습니다. - -`sandbox: None`은 OS 명령 샌드박스를 파일 도구 인가자로 쓰지 않는다는 -뜻입니다. 무한 I/O라는 뜻이 **아닙니다**. 파일 도구 워크스페이스 범위, -명령 샌드박스, 네트워크 강제는 서로 다른 축입니다. - -살아 있는 `exec_command`가 있어도 `read` / `find`는 허용됩니다 -(`read_while_process_live`, `find_while_process_live`). PathSandbox의 -lstat과 open 사이에 프로세스가 디렉터리를 심링크로 바꿀 수 있습니다. -그 순간의 안전은 앞선 lstat이 아니라 no-follow `LOCAL_FS` I/O입니다. - -`find`는 업스트림 한도(깊이 64, 디렉터리 10,000, 항목 50,000)로 walk한 -뒤 CodeSpace glob과 사용자 limit를 적용합니다. walk가 잘리거나 필터 -결과가 limit를 넘으면 `truncated`가 true입니다. 숨김 디렉터리는 -가지치기하지 않습니다(`prune_hidden_directories: false`). glob이 없을 -때 사용자 limit에서 walk를 멈추는 것은 P1 최적화입니다. P0는 제한된 -전체 walk를 유지합니다. - -운영자가 등록한 `workspace.root`가 trust anchor입니다. `find`와 조상 -검사는 그 루트만 `canonicalize`할 수 있습니다. 그 아래 descendant는 -따르지 않습니다. - -`codespace-fs` `FsError`는 제품 코드와 1:1입니다. `NotFound`는 -`FILE_NOT_FOUND`, `NotDirectory`는 `PATH_NOT_DIRECTORY`, 일반 `Io`는 -`FILE_OPERATION_FAILED`, `SymlinkRejected`는 `SYMLINK_REJECTED`, -`NotRegularFile`는 `SPECIAL_FILE_REJECTED`입니다. `PATH_ESCAPE`는 -workspace/path containment 위반입니다. `../`·절대경로 요청이거나, walk -결과가 워크스페이스 루트에 `strip_prefix`되지 않아 범위 밖이 관찰된 -경우입니다. `find` 루트 canonicalize 실패는 `FILE_OPERATION_FAILED`입니다 -(containment는 유지, operation 실패). rollback 파일시스템 실패도 같은 -매핑이며 `INVALID_PATCH`가 아닙니다. - -## `command/exec`: 형태 대 크레이트 - -핀의 App Server `command/exec` -([`command_exec.rs`](../../third_party/codex/codex-rs/app-server-protocol/src/protocol/v2/command_exec.rs)) -는 **독립** argv API입니다. thread 없음, turn 없음. 필드에는 argv, -선택적 프로세스 id, tty, stdin/stdout 스트리밍, 출력 한도, 타임아웃, -cwd, env, PTY 크기, `sandboxPolicy` / `permissionProfile`이 있습니다. -후속: write, resize, terminate. 스트리밍은 `outputDelta`입니다. - -그 **형태**가 오늘 Runner DTO에 있습니다. PTY spawn은 같은 -`process_id` 뒤에 연결됩니다(`exec_command.tty`, 기본 false, 어댑터 -크기 24x80). `process_resize` / `tty_size`는 **P1**로 남습니다. -게이트웨이가 `cwd: WorkspaceRoot`, 러너 로컬 env 기본값(`PATH` / -`HOME` / `LANG`은 러너 프로세스에서 적용, PTY일 때만 `TERM=xterm`, -게이트웨이 `PATH`나 호스트 절대 cwd를 직렬화하지 않음), 타임아웃, -출력 한도, 정책 요약을 채웁니다. 실제 MCP는 그대로입니다. - -```text -exec_command / write_stdin / read_process / terminate_process -``` +| 실행 환경 | 운영자가 선택하는 실행 위치. 호스트는 구현되어 있으며 등록된 컨테이너 백엔드는 사용 불가 | +| 작업 공간 | `workspace_id`로 선택하는 등록 루트. MCP 파일 경로는 상대 경로 | +| 권한 프로필 | 파일·프로세스 행동의 허용 범위를 게이트웨이가 결정 | +| 패치 작업 | `operation_id`와 선택적 중복 실행 방지 키로 저장하는 요청 | +| 프로세스 | 서버가 발급하는 명령 핸들. 메모리에만 보관 | +| 논리적 작업 | 작업과 사용자 지시 큐. 전송 세션과 별개 | -모델은 어댑터 토폴로지가 아니라 MCP에서 이것을 배웁니다. -`initialize.instructions`는 전역 불변식입니다. 워크스페이스를 고르면 -`workspace_info.execution`이 실제 capability입니다. `exec_command` 결과는 -`dispatch_status`를 실습니다. architecture 매뉴얼, Codex crate 그래프, -UDS 와이어를 클라이언트 계약에 넣지 마세요. +`environment_id`는 MCP 도구 인자가 아닙니다. 모델이 전달한 프록시 URL이나 Codex 사용자 설정이 실행 권한의 근거가 되지 않습니다. -`output_combined=true`는 `read_process`가 하나의 combined output stream만 -노출한다는 뜻입니다. stdout/stderr origin은 보존하지 않습니다. pipe -프로세스는 stdout과 stderr를 독립적으로 pump하므로 둘 사이의 상대 -순서는 보장하지 않습니다. PTY 출력은 terminal master stream입니다. + + + + -`codex-exec`(제품 exec 흐름)를 가져오거나 App Server를 넣지 **마세요**. -`codex-exec-server-protocol`은 내부 워커 DTO 후보입니다. -`codex-exec-server`는 실험적 백엔드(`codex-api` / `codex-config`)이며 -영구 거절은 아닙니다 -([codex-reuse.md](codex-reuse.md)). 샌드박스 정책을 “Codex 사용자 -설정”에서 기본값으로 두지 **마세요**. 게이트웨이는 이미 허용된 요청을 -러너 DTO로 매핑합니다. `codex-process-hardening`, `codex-utils-pty`, -`codex-uds`(전송 프리미티브, RPC는 CodeSpace)를 선호하세요. PathSandbox -범위 아래 `codex-file-system`(`crates/file-system` → `LOCAL_FS`, -no-follow I/O와 제한된 walk)을 가져왔습니다. -`codex-linux-sandbox`는 `crates/linux-sandbox` **바이너리**로 -가져왔습니다(dev-dep에 `codex-core` 포함, 제품 그래프에서는 빼 둘 -것). 러너는 `crates/linux-sandbox-protocol`로 `prepare` / -`run --plan`을 말합니다. 네트워크 축이 생기면 -`codex-network-proxy`를 보세요. 컨테이너가 그 서브그래프를 -대체하지 않습니다. - -App Server 스트리밍 프로세스는 연결 범위이며 그 연결이 닫히면 죽습니다. -CodeSpace는 **MCP 요청 수명 ≠ 프로세스 수명**을 유지합니다. `process_id`는 -서버가 발급하고 애플리케이션 상태로 저장합니다. MCP 요청이 끝나도 살아 -있는 프로세스를 죽이지 않습니다. 선택적 UDS 경로는 다릅니다. 게이트웨이 ↔ -워커는 1:1입니다. UDS 연결 끊김이나 게이트웨이 종료는 워커를 죽입니다 -(호스트 자식도 죽습니다). `process_id`는 워커 죽음 이후 살아남지 않습니다. -러너 `Replay`는 같은 연결 프리미티브이며 연결 끊김 복구가 아닙니다. - -## 승인과 MCP 리비전 - -권한 부족은 오늘 **정책 거절**입니다(`ErrorBody`). 도구 인자 -`{ "network": true }`의 조용한 부여가 아닙니다. - -핵심 프로토콜은 **MCP 2025-11-25** `tools/call`로 남습니다 -([protocol-compatibility.md](protocol-compatibility.md)). MRTR과 Tasks는 -2026-07-28 점진적 향상입니다. exec나 patch에 필수가 되면 안 됩니다. - -추가 권한을 나중에 설계할 때: - -1. 명시적 도구(`approval_create` / `approval_resolve` / - `operation_resume`)를 선호해 2025-11-25 클라이언트가 동작하게 하세요. -2. 같은 상태를 0728 호스트용 MRTR `input_required`에 선택적으로 매핑하세요. - -사람 / 구성된 정책이 모델 요청과 OS exec 사이에 있습니다. 부여를 -결정하려고 모델을 호출하지 않습니다. - -장시간 **비대화형** 작업은 나중에 MCP Tasks를 쓸 수 있습니다. -**대화형** 작업은 `process_id`를 유지합니다. Tasks가 프로세스 핸들을 -대체하면 안 됩니다. - -## 스케줄러 (단일 쓰기 잠금 이후) - -오늘은 워크스페이스 쓰기 잠금 하나와 셸 점유로 충분합니다. App Server는 -자원별로 직렬화합니다(배타 대 공유 읽기). 목표 범위는 Environment, -Workspace, Path, Process, Operation, Watch이며 Thread가 아닙니다. -`crates/store`는 이제 그 키를 위한 메모리 자원 직렬화기를 씁니다. -SQLite 스키마는 그대로입니다. MVP는 `apply_patch`에 요청 소유 배타, -라이브 셸에 프로세스 소유 배타를 씁니다(`WORKSPACE_BUSY`). -`ProcessExited`(또는 프로세스 내부 종료)가 `release_process`를 호출합니다. -확인된 UDS 워커 죽음은 프로세스 소유 임대를 **모두** 풉니다. 응답 -유실/모호함만으로는 풀지 않습니다. -`read` / `find`는 잠금이 없습니다. Shared-read는 타입만 유지합니다. - -## `fs/watch`와 검색 - -`fs/watch`를 모델 도구로 노출하지 마세요. 외부 편집기 범프가 버전을 -무효화하고 `apply_patch`가 `expected_versions` / 이후 `STALE_READ`로 -실패할 수 있도록 내부에서 사용하세요. - -퍼지 검색 **세션**은 TUI 입력 UX입니다. MCP는 `find` / 이후 -`find_files(query, workspace_id, limit)`로 유지하세요. 그 계약 뒤의 -엔진으로 `codex-file-search`를 선호하고 세션 프로토콜은 버리세요. - -## 훅과 스킬 - -훅은 로컬이고 결정적이며 모델을 호출할 수 없을 때만 허용됩니다 -(`before_patch` 정책, `after_patch` fmt, 감사). Responses API로 코드를 -검토하는 훅은 금지입니다. - -스킬은 숨은 에이전트에 자동 주입되지 않습니다. 추가한다면 **호스트**가 -읽기로 고르는 MCP 리소스나 프롬프트입니다. - -다운스트림 MCP 연합(이 서버가 MCP 클라이언트)은 P3입니다. 모델은 없지만 -인증과 도구 이름 충돌이 비쌉니다. - -## 가져오기 / 빼기 (개념) - -| 가져오기 (기반) | 빼기 (에이전트 런타임) | -| --- | --- | -| V4A parse/verify/apply | `thread/*`, `turn/*`, steer-as-turn | -| Standalone command/exec **shape** | `codex-exec` crate, App Server embed | -| PTY / UDS / linux-sandbox / hardening subgraph | Homegrown Landlock/seccomp/PTY/UDS by default | -| Filesystem mechanics under PathSandbox scope | Replacing PathSandbox wholesale; `codex-protocol` in core | -| Process manager / PTY helper | Connection-scoped process death | -| Sandbox **policy object** (gateway fills) | User Codex config as default allow | -| Permission profile **shape** in `crates/policy` | `permissionProfile` from the model or Codex session | -| Environment as exec location | Agent / account / model provider | -| Resource serialization | Thread-keyed queues | -| Internal fs/watch | Watch as an MCP tool | -| Search engine, not session RPC | Absolute-path `fs/writeFile` on the wire | -| Deterministic hooks | Hook → model | -| Operation / diff / audit | Conversation compaction, memory, review, Guardian, multi-agent, Goal | +## 실행과 결과 관측 -개념 대응(타입을 가져오지 말 것): Thread → workspace/operation 이력, -Turn → Operation, Interrupt → cancel, Turn diff → `operation_diff`, -Approval → policy + human, Attachment → artifact 리소스. +게이트웨이는 내부 Runner 요청에 작업 공간 루트 cwd, 러너 환경 기본값, 시간·출력 제한, PTY 선택, 정책을 채웁니다. 현재 터미널 옵션으로 공개된 것은 `tty`뿐입니다. 공개 호출은 임의의 cwd·환경변수·제한 시간 변경을 받지 않습니다. 기본값은 [운영](operations.md), 결과 처리는 [Agent Loop 연동](agent-integration.md)을 참고하세요. -## 로드맵 (구현은 나중) +작업 공간 잠금은 패치와 명령이 동시에 파일을 변경하지 못하게 합니다. 명령 실행 중에도 읽기와 검색은 가능하므로 파일 I/O는 사전 경로 검사에만 의존하지 않고 파일을 여는 시점의 심볼릭 링크 변경도 거부해야 합니다. Runner 파일 작업은 `codespace-fs`, 패치 적용은 별도 패치 도우미를 사용합니다. -이 기반의 P0 코드는 들어와 있습니다. Runner exec DTO **형태** -(`RunnerCwd::WorkspaceRoot`, 러너 로컬 env 기본값), -`crates/policy`의 `PermissionProfile`(`process_exec`)과 Environment, -자원 직렬화기(요청 vs 프로세스 소유), 선택적 `UdsRunner` + -`codespace-codex-runtime`(process-hardening + UDS), 격리된 -`crates/pty` → `codex-utils-pty`, 격리된 `crates/file-system` → -`LOCAL_FS`, 격리된 `crates/linux-sandbox` 바이너리 → `codex-linux-sandbox` -(프로세스 경계). -실제 MCP 도구 **이름**은 그대로입니다. -`exec_command`에 선택적 `tty`(기본 false)가 있습니다. +UDS 전송과 Linux 샌드박스 준비는 서로 다른 프로토콜과 실패 경계를 가집니다. UDS 변경 요청이 일부만 전달되면 결과가 불확실할 수 있습니다. 연결이 끊겼다는 이유만으로 새 변경 요청을 보내지 마세요. 프로세스·격리 규칙 전체는 [러너 격리](runner-isolation.md)에 설명합니다. -**P0** — 착수했거나 다음 서브그래프 WP: `codex-apply-patch`(완료), -Runner DTO의 exec 런타임 **형태**(완료), `crates/policy`의 -PermissionProfile 도메인(완료), Environment 도메인(운영자 등록, 도구 -인자 아님)(완료), 자원 직렬화기(완료), process-hardening + UDS를 받는 -전송(`UdsRunner`)(완료, 선택적), PTY I/O 백엔드(완료), PathSandbox 아래 -파일시스템 역학(완료), Linux command sandbox(완료: 헬퍼 프로세스 경계, Restricted -hard deny, Enabled 관리 프록시). P0 실행 서브그래프는 완료입니다. + + + + + + + + + + -**P1** — 작업 상태 기계 / diff 원장, 승인 폴백 도구, 내부 watch, 더 -풍부한 프로세스 핸들(resize, caps), 연결 끊김 정책. +## 아직 제공하지 않는 기능 -**P2** — 기존 MCP 계약 뒤 `codex-file-search`로 `find` 품질, 결정적 -훅, 리소스 또는 프롬프트로서의 스킬. +MCP에는 프로세스 종료 코드와 명시적 출력 유실 정보가 없습니다. PTY 크기 변경, 파일 범위·페이지 인자, 영속적인 프로세스 복구, 컨테이너·원격 실행, 승인 후 재개 도구, 자원 큐 스케줄러도 제공하지 않습니다. 내부 타입이나 협상된 프로토콜 플래그가 존재한다고 해당 기능을 호출할 수 있는 것은 아닙니다. -**P3** — 원격 환경, MCP 연합, 아티팩트 레지스트리. +## 구현 경계 유지 -다음 **코드** WP는 기존 트레이트 뒤의 P1(작업 상태 기계 / diff 원장)이며, -`apply_patch`를 게이트웨이 RPC로 쪼개지 않습니다. Sandbox / network는 기본 자체 OS 스택이 아닙니다 -([codex-reuse.md](codex-reuse.md)). +`check-no-model-deps.sh`는 핵심 계층의 의존성 선언과 일부 소스 패턴, 어댑터 의존성 허용 목록을 검사합니다. 업스트림 소스 전체를 검사하거나 가능한 모든 모델 호출의 부재를 증명하는 도구는 아닙니다. CI는 Codex 고정 커밋, 어댑터 빌드·테스트, Runner에서 샌드박스 도우미 라이브러리로 향하는 금지된 의존성도 확인합니다. 릴리스 검증은 [업스트림 업데이트](upstream-update.md)를 참고하세요. diff --git a/docs/ko/index.md b/docs/ko/index.md index 56e9083..ecf6d19 100644 --- a/docs/ko/index.md +++ b/docs/ko/index.md @@ -1,25 +1,27 @@ -# CodeSpace 문서 + + + +# CodeSpace 문서 안내 [English](../index.md) | [한국어](index.md) -CodeSpace는 개인용 실행 도구 MCP 서버입니다. 바깥 클라이언트가 무엇을 -할지 판단합니다. 이 프로세스는 모델을 호출하지 않습니다. 파일을 읽고, -Codex 형식 패치를 적용하며, 등록된 워크스페이스에서 관리형 명령을 -실행합니다. +CodeSpace를 직접 구성한 코딩 에이전트의 실행 계층으로 연결하는 방법을 안내합니다. 먼저 로컬 작업 공간을 등록하고 MCP로 연결한 다음, 에이전트에 실행 결과 처리와 복구 규칙을 적용하세요. + + + + +## 목적에 맞는 문서 찾기 -## 다음 단계 +| 필요한 정보 | 문서 | +| --- | --- | +| 제품의 역할과 제약 이해 | [시작하기](../../README.ko.md) | +| 설치와 작업 공간 연결 | [운영 가이드](operations.md) | +| 읽기·수정·실행·복구 루프 구성 | [Agent Loop 연동](agent-integration.md) | +| ChatGPT 연결 가능성 검토 | [ChatGPT 연결 상태](chatgpt-connector.md) | +| 모듈별 책임 이해 | [아키텍처](architecture.md), [실행 계약](execution-substrate.md) | +| 권한과 격리 조건 확인 | [보안 모델](security-model.md), [러너 격리](runner-isolation.md) | +| 프로토콜과 도구 결과 처리 | [프로토콜 호환성](protocol-compatibility.md), [패치 동작](behavior-differences.md), [오류 코드](error-codes.md) | +| Codex 의존성 유지보수 | [재사용 범위](codex-reuse.md), [고정 버전](upstream-lock.md), [업데이트 절차](upstream-update.md) | +| 문서 수정과 게시 | [문서 유지보수](documentation.md) | -- [시작하기](../../README.ko.md) — 도구, 실행 명령, 라이선스 -- [운영](operations.md) — 설치, 실행, 로그, 복구 -- [ChatGPT 커넥터](chatgpt-connector.md) — stdio, HTTP, 미검증 계정 검사 -- [아키텍처](architecture.md) — 현재와 목표 프로세스 배치 -- [실행 기반](execution-substrate.md) — 모델 없는 실행 불변식 -- [프로토콜 호환성](protocol-compatibility.md) — MCP 2025-11-25 기준 -- [동작 차이](behavior-differences.md) — 제품 정책 대 크레이트 기본값 -- [보안 모델](security-model.md) — 게이트웨이 정책과 신뢰 경계 -- [러너 격리](runner-isolation.md) — 현재 호스트 exec와 목표 Linux 컨테이너 -- [오류 코드](error-codes.md) — 전송 실패 대 실행 오류 -- [Codex 재사용](codex-reuse.md) — 제품 대 프리미티브 -- [업스트림 고정](upstream-lock.md) — 핀된 Codex 서브모듈 -- [업스트림 핀 갱신](upstream-update.md) — 의도적 릴리스 절차 -- [문서 사이트](documentation.md) — 영·한 레지스트리와 Pages +지원 동작은 실제 구현을 기준으로 설명합니다. 테스트 안내는 검사 범위를 나타내며, 특정 설치 환경이나 외부 계정에서 검증을 마쳤다는 뜻은 아닙니다. 실제 사용할 작업 공간의 실행 조건은 `workspace_info` 응답에서 확인하세요. diff --git a/docs/ko/operations.md b/docs/ko/operations.md index 58b8236..801930d 100644 --- a/docs/ko/operations.md +++ b/docs/ko/operations.md @@ -1,81 +1,53 @@ -# 운영 + + + +# 설치와 운영 [English](../operations.md) | [한국어](operations.md) -깨끗한 클론에서 로컬 CodeSpace를 재현합니다. 설치하고 게이트웨이를 -시작한 뒤 `workspace_info` → `read` → `apply_patch` → `exec_command` -순서로 진행합니다. 이것은 **개인 단일 사용자** 배치입니다. 멀티테넌트 -SaaS가 아니고 완전한 OAuth 서버도 아닙니다. +macOS 또는 Linux에서 개인용 단일 사용자 서버를 준비하는 방법입니다. 설치 후 도구 호출 순서는 [Agent Loop 연동](agent-integration.md)을 참고하세요. -공개 호스트 이름에 대한 ChatGPT Custom Connector는 **검증하지 않았습니다**. -[chatgpt-connector.md](chatgpt-connector.md)를 보세요. + ## 설치 -Rust 1.88+, Git, 그리고 (Linux 격리 픽스처용) Docker가 필요합니다. +Git과 CI에서 사용하는 최신 Rust stable 도구 체인을 준비하세요. 루트 manifest에는 Rust 1.88이 선언되어 있지만 Codex 어댑터 전체의 최소 빌드 버전으로 검증된 값은 아닙니다. 고정된 업스트림 소스는 Rust 1.95.0을 지정합니다. macOS에는 Xcode 명령줄 도구를 설치하세요. Linux 명령 격리에는 샌드박스 도우미, bubblewrap, 필요한 네임스페이스를 생성할 권한이 추가로 필요합니다. Docker는 별도의 [컨테이너 실험 구성](../../deploy/README.md)을 사용할 때만 필요합니다. ```bash git clone --recurse-submodules https://github.com/novelKR/CodeSpace.git cd CodeSpace -# If you already cloned without submodules: -# git submodule update --init --recursive +# 이미 복제한 저장소에서도 서브모듈을 초기화합니다. +git submodule update --init --recursive +cargo build --locked -p codespace-server --bin codespace-mcp --release +cargo build --locked --manifest-path crates/patch/Cargo.toml --bin codespace-patch --release +mkdir -p dist +cp target/release/codespace-mcp dist/ +cp crates/patch/target/release/codespace-patch dist/ ``` -Codex 핀은 [upstream-lock.md](upstream-lock.md)의 커밋에 있는 -`third_party/codex`입니다. Codex `main`으로 `git submodule update --remote`를 -하지 마세요. 제품 런타임은 게이트웨이 밖에 둡니다. -[codex-reuse.md](codex-reuse.md)와 -[execution-substrate.md](execution-substrate.md)를 보세요. +서버가 패치 도우미를 찾을 수 있도록 두 실행 파일을 같은 디렉터리에 두세요. 다른 위치에 두려면 `CODESPACE_PATCH_BIN`에 절대 경로를 지정합니다. 도우미에서 Codex 패치 라이브러리를 호출하므로 서버만 빌드해서는 패치를 적용할 수 없습니다. -게이트웨이가 패치 헬퍼와 Linux 샌드박스 헬퍼를 자기 옆에서 찾을 수 -있도록 게이트웨이와 헬퍼를 **같은** 디렉터리에 빌드하세요 -(`CODESPACE_PATCH_BIN` / `CODESPACE_LINUX_SANDBOX_BIN`을 설정해도 -됩니다). UDS 워커는 선택입니다(`CODESPACE_RUNTIME_BIN`). +Linux 명령 격리를 사용하려면 다음 도우미도 빌드하여 서버 옆에 둡니다. ```bash -cargo build -p codespace-server --bin codespace-mcp --release -cargo build --manifest-path crates/patch/Cargo.toml --bin codespace-patch --release -cargo build --manifest-path crates/linux-sandbox/Cargo.toml --bin codespace-linux-sandbox --release -mkdir -p dist -cp target/release/codespace-mcp dist/ -cp crates/patch/target/release/codespace-patch dist/ +cargo build --locked --manifest-path crates/linux-sandbox/Cargo.toml --bin codespace-linux-sandbox --release cp crates/linux-sandbox/target/release/codespace-linux-sandbox dist/ -# Optional Unix-socket worker (not the default exec path): -cargo build --manifest-path crates/codex-runtime/Cargo.toml --bin codespace-codex-runtime --release -cp crates/codex-runtime/target/release/codespace-codex-runtime dist/ ``` -`codespace-patch`는 호스트 자식 프로세스입니다. 핀된 Codex 크레이트를 -**프로세스 내부에서** 호스팅합니다. 업스트림 독립 `apply_patch` 바이너리가 -아니고 폐기된 `native/patch-worker`도 아닙니다. `codespace-codex-runtime`은 -`codex-process-hardening`과 `codex-uds`로 비공개 Unix 소켓을 바인드한 뒤 -`InProcessRunner`를 실행합니다. hardening은 워커/헬퍼 **프로세스** -강화입니다(`main` 첫 줄 `pre_main_hardening()`, `ctor` 없음). command -sandbox가 아닙니다. 기본 `exec_command`는 여전히 프로세스 내부 호스트 -spawn입니다. Linux에서 `CODESPACE_LINUX_SANDBOX_BIN`(또는 게이트웨이 옆 -`codespace-linux-sandbox`) probe가 성공하면 그 spawn은 짧은 `prepare` -다음 `helper run --plan`입니다. Codex argv는 러너에 들어오지 않습니다. -`workspace_info.execution.isolation.command_sandbox`는 그때만 -`linux-sandbox`이고, 아니면 `none`입니다. Restricted 네트워크는 그때 -OS에서 강제됩니다(`network.enforcement=enforced`). Enabled도 같은 -enforcement이며 헬퍼 안의 관리 HTTP 프록시를 씁니다. 헬퍼가 없으면 -`PROCESS_SPAWN_FAILED`이지 호스트 FullAccess가 아닙니다. -`exec_command.tty` 기본값은 false(파이프)입니다. -`tty: true`는 24x80 PTY를 붙입니다. Exec DTO cwd는 `WorkspaceRoot`이며 -`PATH` / `HOME` / `LANG`은 러너 프로세스에서 적용합니다(PTY일 때 -`TERM=xterm`). - -## 워크스페이스 레지스트리 - -[workspaces.example.json](../workspaces.example.json)을 복사하고 `root`를 -**등록한 실제 디렉터리**로 지정하세요. 모델은 워크스페이스를 추가할 수 -없습니다. +빌드 성공만으로 격리가 활성화되었다고 판단할 수 없습니다. 연결 후 `workspace_info.execution.isolation.command_sandbox`를 확인하세요. 자세한 내용은 [활성 조건과 실패 처리](runner-isolation.md)에 있습니다. + + + + +## 작업 공간 등록 + +사용할 프로젝트 디렉터리를 먼저 만들고, 레지스트리는 그 밖에 둡니다. 아래 `/absolute/path/to/project`를 실제 프로젝트 경로로 바꾸세요. ```json { "workspaces": { "demo": { - "root": "/absolute/path/to/your/project", + "root": "/absolute/path/to/project", "profile": "workspace-write", "network": "restricted" } @@ -83,151 +55,98 @@ enforcement이며 헬퍼 안의 관리 HTTP 프록시를 씁니다. 헬퍼가 } ``` -프로필: `read-only`(기본 의도) 또는 `workspace-write`. `host-admin`은 -제품 프로필이 아닙니다. 선택적 운영자 `network`는 `restricted`(기본) -또는 `enabled`이며 `environment`처럼 워크스페이스 JSON이지 도구 인자나 -`{ "network": true }`가 아닙니다. Enabled는 Linux 헬퍼가 필요합니다. -선택적 운영자 `environments`는 `host` 또는 -`linux-container`를 등록할 수 있습니다. 생략하면 암시적 로컬 호스트입니다. -`linux-container`는 exec 경로가 아닙니다. 도구와 `workspace_info`에는 -`environment_id`가 없습니다. `workspace_id`로 `workspace_info`를 호출하면 -실제 execution 계약(`execution`)을 읽습니다. 정책 대 백엔드 지원, -`files.*.available`과 `process.available`(권한과 백엔드 지원만, -occupancy 아님 — `exec_command`나 `apply_patch`는 여전히 -`WORKSPACE_BUSY`일 수 있음; 도구 존재는 `tools_exposed`), process가 -가능할 때 resize 없는 고정 24x80 PTY, mutation lease / -`WORKSPACE_BUSY`, 워크스페이스 범위 파일 도구 대 광고된 Linux command -sandbox, 헬퍼 probe가 성공하면 OS가 강제하는 `network.policy` -(`restricted` 또는 `enabled`)입니다. -`output_combined=true`는 `read_process`가 하나의 combined stream만 -노출하고 stdout/stderr origin을 보존하지 않는다는 뜻입니다. -`exec_command`는 `dispatch_status`(`confirmed` 또는 -`unknown`)를 반환합니다. `unknown` patch/exec는 실행됐을 수 있으니 새 -`operation_key`로 같은 mutation을 재시도하지 마세요. - -## 게이트웨이 실행 - -stdio(Cursor / 로컬 MCP 호스트): +CodeSpace 저장소의 `workspaces.json`으로 저장합니다. `read-only`는 읽기를 허용하고, `workspace-write`는 패치와 명령 실행도 허용합니다. 경로와 권한은 운영자가 등록합니다. 도구에 전달하는 `workspace_id`는 등록 항목을 선택할 뿐, 권한을 부여하지 않습니다. + +`network` 기본값은 `restricted`입니다. `enabled`를 사용하려면 Linux 도우미가 필요하며, 지원되는 HTTP 통신은 관리 프록시를 거칩니다. 호스트 네트워크에 무제한 접근하는 설정이 아닙니다. 도우미를 사용할 수 없으면 `enabled` 실행은 실패합니다. `restricted`이고 도우미가 없으면 호스트 실행은 가능하지만 네트워크 제한은 OS 수준에서 강제되지 않습니다. 실제 환경의 적합성은 응답의 정책 집행 상태를 확인해 판단하세요. + + + + +## 서버 시작 + +CodeSpace 저장소에서 절대 경로와 패치·작업 기록 저장 위치를 설정합니다. ```bash -export CODESPACE_CONFIG="$PWD/docs/workspaces.example.json" -export CODESPACE_OPERATIONS_DB="$PWD/data/operations.sqlite" +export CODESPACE_CONFIG="$PWD/workspaces.json" mkdir -p data -./dist/codespace-mcp +export CODESPACE_OPERATIONS_DB="$PWD/data/operations.sqlite" +export CODESPACE_PATCH_BIN="$PWD/dist/codespace-patch" +# 기본 제한은 30초입니다. 운영자가 빌드에 맞는 시간을 지정합니다. +export CODESPACE_PROCESS_TIMEOUT_SECS=300 ``` -Streamable HTTP(선택적 정적 Bearer — 실험 전용, 절대 로그하지 마세요): +stdio를 사용하려면 MCP 클라이언트가 위 환경변수를 전달하고 `dist/codespace-mcp`의 절대 경로를 실행하도록 설정합니다. 서버의 기본 전송 방식은 stdio입니다. 터미널에서 `./dist/codespace-mcp`를 실행하면 MCP 입력을 기다리며, 대화 화면이 열리지는 않습니다. 로그는 stderr로, MCP 메시지는 stdout으로 출력합니다. + +Streamable HTTP를 사용하려면 다음 프로세스를 계속 실행해 둡니다. ```bash export CODESPACE_HTTP_HOST=127.0.0.1 export CODESPACE_HTTP_PORT=8787 -# export CODESPACE_HTTP_TOKEN="replace-me" -export CODESPACE_CONFIG="$PWD/docs/workspaces.example.json" -export CODESPACE_OPERATIONS_DB="$PWD/data/operations.sqlite" +# 클라이언트가 Bearer 토큰을 보낸다면 CODESPACE_HTTP_TOKEN을 안전하게 설정합니다. ./dist/codespace-mcp --http ``` -엔드포인트: `http://127.0.0.1:8787/mcp`. 사용자 Inbox JSON은 -`http://127.0.0.1:8787/inbox`이며 **같은** HTTP 리스너와 Bearer를 -사용합니다. stdio 전용 모드는 `/inbox`를 노출하지 않습니다. 초안은 -`POST /inbox/intents/{id}/queue` 전까지 모델에 넘어가지 않습니다. -바인드 주소는 공개 `Host` 헤더와 같지 않습니다. 리버스 프록시는 외부 -호스트 이름을 따로 허용하세요. `0.0.0.0`을 그 이름으로 취급하지 마세요. - -`CODESPACE_OPERATIONS_DB`가 없으면 작업과 의도 큐는 메모리에 있고 -재시작 후 **남지 않습니다**. 프로세스 핸들은 재시작 후 절대 남지 -않습니다. - -선택적 러너 워커(여전히 호스트 exec이며 Linux 격리가 아님). UDS는 -1:1입니다. 게이트웨이가 `RuntimeProcess`(자식, 비공개 0700 디렉터리, -`$dir/runner.sock`)를 소유합니다. 재연결은 없습니다. -`--runner` / `CODESPACE_RUNNER`의 허용 값은 `in-process`와 `uds`뿐입니다. -`--runner-dir` / `CODESPACE_RUNNER_DIR`은 그 unique leaf의 부모가 될 -수 있습니다. `/`, `/tmp`, `/var/tmp`, `$HOME`을 디렉터리 자체로 주면 -거절합니다. `--runner-socket`은 이미 떠 있는 워커에 연결할 때만 쓰며 -부모 path를 chmod하지 않습니다. +MCP 클라이언트에서 `http://127.0.0.1:8787/mcp`로 연결합니다. 같은 포트의 `/inbox`는 JSON API이며 동일한 선택적 Bearer 인증을 사용합니다. stdio 전용 모드에는 `/inbox`가 없습니다. 서버는 `.env.example`을 자동으로 읽지 않습니다. + +공개 HTTPS와 리버스 프록시는 별도로 검증해야 합니다. 현재 HTTP Host 허용 목록은 루프백과 바인드 호스트 값으로 구성되며, 공개 호스트를 따로 지정하는 설정은 없습니다. 따라서 공개 도메인 요청이 거부될 수 있습니다. `0.0.0.0`에 바인드하는 것과 외부 호스트 이름을 허용하는 것은 다릅니다. + + + + +## 첫 연결 확인 + +MCP 초기화와 도구 목록 조회를 마친 뒤 `workspace_info`를 `{"workspace_id":"demo"}`로 호출합니다. 파일·프로세스 사용 가능 여부가 의도한 권한과 맞는지 확인하세요. 이어서 프로젝트의 알려진 파일을 읽고 [작은 패치와 프로세스 예시](agent-integration.md)를 실행합니다. + +`available`은 권한과 백엔드 지원 여부를 나타내며 현재 점유 상태는 포함하지 않습니다. 이후 패치나 명령에서 `WORKSPACE_BUSY`가 발생할 수 있습니다. `linux-container` 환경은 등록할 수 있지만 파일·명령 실행 백엔드는 아직 없습니다. + +## 선택적 Unix 소켓 worker + +worker를 빌드하고 게이트웨이를 시작하기 전에 선택합니다. ```bash +cargo build --locked --manifest-path crates/codex-runtime/Cargo.toml --bin codespace-codex-runtime --release +cp crates/codex-runtime/target/release/codespace-codex-runtime dist/ export CODESPACE_RUNNER=uds -export CODESPACE_RUNNER_DIR="$PWD/data/runner" export CODESPACE_RUNTIME_BIN="$PWD/dist/codespace-codex-runtime" -./dist/codespace-mcp ``` -## MVP 흐름 재현 +worker는 같은 호스트에서 실행하는 별도 프로세스이며 컨테이너가 아닙니다. 게이트웨이가 전용 소켓 디렉터리를 만들고 자식 프로세스를 관리합니다. worker 연결이 끊기거나 게이트웨이가 종료되면 해당 worker의 프로세스도 종료됩니다. 재접속과 프로세스 복구는 지원하지 않습니다. 기본값은 `in-process`입니다. -자동 커버리지(ChatGPT 계정 불필요): + + + + -```bash -cargo test -p codespace-server --test apply -cargo test -p codespace-server --test process -cargo test -p codespace-server --test protocol_compat -cargo test -p codespace-server --test inbox -cargo test -p codespace-server --test e2e -``` +## 로그와 제한, 복구 -이 시험은 패치 적용/디스크 확인과 관리형 프로세스 -(`exec_command` / `read_process` / `WORKSPACE_BUSY`)를 다룹니다. +| 설정 또는 제한 | 동작 | +| --- | --- | +| `RUST_LOG` | stderr 로그 수준. 기본값 `info` | +| `CODESPACE_OPERATIONS_DB` 미설정 | 패치 작업과 지시 큐를 메모리에 보관하며 재시작 시 사라짐 | +| `CODESPACE_PROCESS_TIMEOUT_SECS` | 양의 정수. 기본 30초. 러너 환경에 설정 | +| `CODESPACE_MAX_PROCESSES` | 러너 전체의 실행 중 프로세스 기본 상한 8개. 작업 공간별 점유 규칙도 적용 | +| 프로세스 출력 | 마지막 256 KiB 보관. stdout/stderr를 합치며 MCP 결과에 유실 표시와 종료 코드가 없음 | +| 종료된 핸들 | 기본 최대 15분, 최대 64개 보관. 영구 저장하지 않음 | -수동 stdio: 위 환경 변수로 MCP 클라이언트를 `./dist/codespace-mcp`에 -연결한 뒤, JSON `root`가 존재하고 프로필이 쓰기를 허용한 다음 -`workspace_id: "demo"`에 대해 그 도구들을 호출하세요. +로그와 데이터베이스는 관리 대상 작업 공간 밖에 두세요. stderr 로그의 보관·순환은 운영자가 관리합니다. Bearer 토큰을 로그나 커밋에 넣지 마세요. 데이터베이스를 삭제하면 패치 중복 실행 방지 기록도 사라집니다. -## Linux 격리 픽스처 +패치 응답을 받지 못했다면 `operation_id` 또는 `operation_key` 중 하나만 지정해 `operation_status`를 조회합니다. 재시작 후 미완료 기록은 `unknown`이 되므로 파일을 확인한 뒤 다음 행동을 결정하세요. 프로세스는 `process_id`로 관리하며 `operation_status`로 복구할 수 없습니다. [재시도와 복구 규칙](agent-integration.md)을 참고하세요. -[`deploy/compose.yml`](../../deploy/compose.yml)은 슬리퍼 픽스처입니다. -프로젝트를 uid `10001`로 `/workspace`에 **만** 바인드 마운트합니다. -호스트 `$HOME`, SSH 에이전트 소켓, `/var/run/docker.sock`, 게이트웨이 -`.env`, Bearer 파일, operations SQLite 파일은 마운트하지 않습니다. -`codespace-mcp`를 실행하지 않으며 `exec_command`에 **연결되어 있지 -않습니다**. + + + + -```bash -export CODESPACE_WORKSPACE=/absolute/path/to/your/project -docker compose -f deploy/compose.yml up --build -``` +## 문제 해결 + +| 증상 | 먼저 확인할 항목 | +| --- | --- | +| `WORKSPACE_NOT_FOUND` | 레지스트리 경로, 등록 ID, 서버에 전달된 환경변수 | +| 패치 도우미 시작 실패 | `codespace-patch` 빌드 여부와 절대 경로 | +| `WORKSPACE_BUSY` | 기존 명령이 끝났는지 확인하거나 종료한 뒤 패치 | +| 명령 시간 초과 | 운영자 제한 시간. 긴 빌드가 완료되었다고 가정하지 않기 | +| Linux에서 샌드박스가 없다고 표시 | 도우미 위치, bubblewrap, 네임스페이스 지원 확인 | +| `enabled` 명령 시작 실패 | 사용 가능한 Linux 샌드박스 도우미 필요 | +| HTTP 요청 거부 | `/mcp` 경로, Bearer 헤더, Host 검증 | -게이트웨이는 여전히 호스트에서 실행됩니다. `exec_command`는 -워크스페이스를 cwd로 하는 호스트 프로세스입니다(기본은 파이프, -`tty: true`이면 PTY). - -## 로그 - -- 게이트웨이 로그는 **stderr**로 갑니다(`RUST_LOG` / `tracing`, 기본 - `info`). -- 비밀 키(`authorization`, `token`, `bearer`, …)는 구조화 살균기에서 - 가려집니다. 공유하는 셸 이력 문서에 `CODESPACE_HTTP_TOKEN`을 출력하지 - 마세요. -- stderr 캡처는 직접 순환하거나 잘라내세요. 로그 SaaS는 없습니다. -- operations SQLite 파일은 **패치 작업** 행과 works/intents와 함께 - 커집니다. 프로세스 핸들과 자원 잠금은 휘발성 메모리입니다. - 데이터베이스를 이후 러너 마운트에서 빼 두세요. 삭제하면 멱등 키를 - 잊습니다. - -## 연결 끊김 또는 재시작 후 복구 - -HTTP/JSON-RPC 요청 id ≠ `operation_id` ≠ `process_id` ≠ `work_id`. 잃어버린 -HTTP 응답은 실행 실패가 아닙니다. - -- `apply_patch`를 맹목적으로 다시 실행하지 말고, 서버가 발급한 - `operation_id` 또는 클라이언트 `operation_key` **정확히 하나**로 - `operation_status`를 호출하세요. 둘 다 주거나 둘 다 안 주면 오류입니다. -- 충돌 후 미완료 행은 `unknown`입니다. 서버는 이를 자동 재실행하지 - **않습니다**. UDS에서 `apply_patch` 응답이 유실되면 DB는 `unknown`이며 - 디스크와 모순되는 `rejected`를 쓰지 않습니다. 워크스페이스를 검사한 뒤, - 그 변경이 여전히 필요하면 **새** `operation_key`를 시작하세요. -- 살아있는 `exec_command` 프로세스는 MCP 요청보다 오래 살 수 있습니다. - 발급된 `process_id`로 `read_process` / `terminate_process`를 사용하세요. - 전송이 모호하면 셸 임대를 유지합니다. 워커 `ProcessExited` 뒤에 - `release_process`가 풀어 무한 `WORKSPACE_BUSY`를 막습니다. UDS 연결 - 끊김이나 게이트웨이 종료는 **워커를 죽입니다**(호스트 자식도 함께 - 죽습니다). 확인된 워커 죽음만 프로세스 소유 임대를 풀며 `process_id`는 - 살아남지 않습니다. 러너 `Replay`는 같은 연결 안의 프리미티브이며 - 연결 끊김 복구가 아닙니다. 게이트웨이 재시작 후 옛 OS PID는 - CodeSpace 핸들로 재사용되지 않습니다. - -## 이 문서가 검증하지 않는 것 - -- 이 세션에서 다른 사람의 노트북이나 클라우드 VM에 설치하기 -- ChatGPT Custom Connector OAuth / 공개 HTTPS `Host` 헤더 -- Docker 예제의 커널 탈출 +로컬 전송 테스트는 실제 ChatGPT 연결, 모든 패키지 관리자의 프록시 호환성, 커널·컨테이너 탈출 방어까지 검증하지 않습니다. diff --git a/docs/ko/protocol-compatibility.md b/docs/ko/protocol-compatibility.md index 9d743a0..e89f822 100644 --- a/docs/ko/protocol-compatibility.md +++ b/docs/ko/protocol-compatibility.md @@ -1,111 +1,38 @@ -# 프로토콜 호환성 + + -[English](../protocol-compatibility.md) | [한국어](protocol-compatibility.md) - -CodeSpace는 실행 도구 MCP 서버입니다. 바깥 클라이언트가 무엇을 할지 -고릅니다. 이 프로세스는 모델을 호출하지 않습니다. - -## 핵심 기준 - -| 계층 | 기준 | 참고 | -| --- | --- | --- | -| Protocol | **MCP 2025-11-25** | 1급. CI가 stdio와 Streamable HTTP에서 이 리비전을 강제합니다. | -| Transport | **stdio**와 **Streamable HTTP `/mcp`** | 양쪽 모두 같은 도구 계약입니다. | -| HTTP floor | **2025-03-26** | Streamable HTTP가 있는 첫 리비전. CI 핀은 아닙니다. | -| Progressive enhancement | **2026-07-28** | 선택. 같은 도구 의미. 핵심에 절대 필수 아님. | - -핵심 실행에 필요한 원시 연산: `initialize`, `tools/list`, -`tools/call`. 현재 실제 도구는 `workspace_info`, `read`, `find`, -`apply_patch`, `operation_status`, `exec_command`, `write_stdin`, -`read_process`, `terminate_process`, `work_open`, `steer_status`, -`steer_claim_next`, `steer_complete`, `work_finish`입니다. 여전히 -`tools/call`을 사용합니다. 사용자 초안과 재정렬은 MCP가 아니라 HTTP -`/inbox`에 있습니다. +# MCP 프로토콜 호환성 -## 핵심이 요구하면 안 되는 것 - -이 2026-07-28(및 관련) 기능은 나중에 UX나 가속으로 협상할 수 있습니다. -**핵심 계약의 일부가 아니며** `tools/call`을 막으면 안 됩니다. - -- MRTR (multi-round-trip requests) -- Tasks -- subscriptions -- `Mcp-Method` / `Mcp-Name` 헤더 라우팅 (SEP-2243) -- 애플리케이션 상태로서의 2026-07-28 무상태 수명주기 - -인증과 라우팅은 Bearer 미들웨어(선택)와 `/mcp` → rmcp 도구 디스패치로 -남습니다. 디스패치는 본문에서 JSON-RPC 메서드와 도구 이름을 읽습니다. -`Mcp-Name`으로 라우팅하지 않습니다. +[English](../protocol-compatibility.md) | [한국어](protocol-compatibility.md) -`crates/domain`, `crates/policy`, `crates/patch`, `crates/store`, `crates/runner`는 -`ProtocolVersion`이나 `NegotiatedFeatures`를 가져오지 않습니다. 서버 -어댑터 `crates/server/src/protocol.rs`가 협상된 리비전을 향상 플래그로 -매핑합니다. 그 플래그가 참이어도 핸들러는 `tools/call`을 유지합니다. -work/steer는 애플리케이션 상태(`work_id` / `intent_id`)이며 MCP Tasks, -MRTR, subscriptions가 아닙니다. +일반적인 MCP 초기화, 도구 조회, `tools/call`을 사용합니다. stdio와 `/mcp`의 Streamable HTTP는 같은 도구를 제공합니다. 작업·지시 큐는 애플리케이션 상태이며 전송 세션과 별개입니다. -이후 **승인** 또는 장시간 **Tasks**( -[execution-substrate.md](execution-substrate.md) 참고)는 2025-11-25 -`tools/call` 경로를 유지해야 합니다. 추가 권한은 현재 정책 거절입니다. -MRTR을 요구하기 전에 미래의 `approval_*` 폴백이 옵니다. 대화형 -프로세스는 `process_id` 핸들로 남습니다. Tasks가 이를 대체하면 안 -됩니다. + + + + + + + + -## 2026-07-28 +## 지원 기준 -클라이언트가 2026-07-28을 협상하면 서버는 향상 플래그를 광고할 수 -있습니다. 실제 도구의 의미는 2025-11-25와 동일합니다. 이 리비전은 -**점진적 향상 전용**입니다. +저장소는 두 전송 방식에서 MCP `2025-11-25`를 명시적으로 테스트합니다. 고정된 SDK를 통해 `2026-07-28` 협상도 이전 버전으로 대체하지 않고 테스트합니다. 새 버전 협상이 다른 실행 동작을 추가하지는 않습니다. 이는 저장소의 호환성 검증 대상이며 클라이언트와 서버가 공통으로 지원하는 버전을 협상해야 합니다. -2026-07-28을 선호하고 2025-11-25로 폴백하는 기존 Auto 시험은 폴백을 -증명합니다. 2025-11-25 전용 커버리지를 **대체하지 않습니다**. +HTTP 전송의 최소 기준은 `2025-03-26`입니다. 이전 `2024-11-05`의 HTTP+SSE 방식은 여기서 지원하지 않습니다. Tasks, 구독, 여러 번의 요청 왕복, 헤더 기반 도구 라우팅은 기본 실행 경로의 필수 조건이나 구현 기능이 아닙니다. 서버는 본문의 JSON-RPC 메서드와 도구 이름으로 요청을 처리합니다. -## 범위 밖 +## 클라이언트가 확인할 정보 -- **2024-11-05 HTTP+SSE.** 목표가 아닙니다. `rmcp` 3.x는 그 전송을 - 제공하지 않습니다. -- MRTR, Tasks, subscriptions 구현. 선택적 점진적 향상으로 남으며 - work/steer, exec, patch, 또는 이후 승인 흐름에 **필수가 아닙니다**. -- 브라우저 Inbox UI(이 릴리스에는 HTTP `/inbox` JSON이 있습니다). +`initialize.instructions`를 읽고 도구를 조회한 뒤 등록 ID로 `workspace_info`를 호출하세요. `execution` 객체는 권한과 백엔드 지원을 구분하고 파일·프로세스 사용 가능 여부, PTY 기능, 직렬화, 샌드박스, 네트워크 집행 상태를 제공합니다. 사용 가능 여부에는 일시적인 점유 상태가 포함되지 않습니다. -## 시험 +도구 인자, 결과 해석, 재시도 규칙은 [Agent Loop 연동](agent-integration.md)을 참고하세요. 지시 큐는 MCP Tasks가 아닌 `work_id`와 `intent_id`를 사용합니다. 프로토콜 플래그는 추가 작업 공간 권한을 부여하지 않습니다. -`crates/server/tests/protocol_compat.rs`가 핀합니다. + + -1. stdio와 HTTP에서 강제 **2025-11-25** (`ClientLifecycleMode::Initialize` - + `with_protocol_version(V_2025_11_25)`). -2. HTTP와 stdio에서 강제 **2026-07-28**, 레거시 폴백 **없음** - (`Auto { preferred_versions: [V_2026_07_28], legacy_version: None }`). +## 테스트 -핸드셰이크 후 `tools/list`는 `workspace_info`를 포함하고 `tools/call`은 -기존 페이로드 계약과 맞습니다. 강제 2025-11-25는 `read` / `apply_patch` / -`exec_command` / `operation_status`와 work/steer 체크포인트(`work_open` -→ `/inbox` queue → `steer_claim_next` → `work_finish`)도 다룹니다. 강제 -2026-07-28은 같은 `tools/call` 표면을 사용합니다. +`crates/server/tests/protocol_compat.rs`는 stdio와 HTTP에서 검증 대상 버전을 각각 강제한 뒤 도구 조회·호출을 검사합니다. 새 버전을 우선하되 이전 버전도 허용하는 테스트는 별개의 근거이며 기준 버전을 강제한 테스트를 대신하지 않습니다. -클라이언트 쪽 실행 의미는 그 표면에 남습니다. `initialize.instructions`는 -전역 불변식(요청 수명 ≠ 프로세스 수명, host는 OS sandbox가 아님, -강제되지 않은 네트워크는 허가가 아님)을 말합니다. 네트워크 정책 값을 -단정하지 않습니다. 실제 정책은 `workspace_info.execution.network`가 -보고합니다. `workspace_id`가 있는 -`workspace_info`는 `execution` 객체(권한 대 백엔드 지원, -`files.*.available`과 `process.available`은 권한과 백엔드 지원이지 -occupancy가 아님, PTY, mutation lease, 격리, 네트워크)를 더합니다. -`files.read.available`과 `files.find.available`은 read 권한과 -`file_read_supported`가 필요합니다. `files.patch.available`은 write -권한과 `file_write_supported`가 필요합니다. `process.available`은 exec -권한과 백엔드 지원이 모두 필요하며 transient occupancy는 포함하지 -않습니다. 도구 존재는 `tools_exposed`입니다. -`output_combined=true`는 `read_process`가 하나의 combined stream만 -노출하고 stdout/stderr origin을 보존하지 않는다는 뜻입니다. -`exec_command` 결과는 -`dispatch_status`(`confirmed` 또는 `unknown`)를 더합니다. 빈 argv는 -`INVALID_COMMAND`입니다(프로세스 dispatch 전 거절). 확인된 spawn 실패는 -`PROCESS_SPAWN_FAILED`입니다. 백엔드가 managed process가 만들어지지 -않았음을 확정한 것이며 `dispatch_status=unknown`과 구분됩니다. Runner -UDS 와이어는 `WIRE_PROTOCOL` 3입니다. `ErrorBody` 제품 코드가 그 JSON을 -타므로 오류 어휘가 바뀌면 protocol number도 바뀝니다. handshake mismatch는 -before-dispatch입니다. Gateway와 worker는 같은 CodeSpace build이며 skew는 -허용하지 않고 탐지합니다. 도구 **이름**은 -늘지 않습니다. `environment_id`, `cwd`, `tty_size`, `process_resize`는 -클라이언트 스키마에 없습니다. +`http_contract`, `stdio_contract`, `transport_contract`는 전송 어댑터를 검사합니다. 로컬 테스트 성공은 실제 ChatGPT 계정이 사용하는 프로토콜 버전의 증거가 아닙니다. [ChatGPT 연결 상태](chatgpt-connector.md)를 참고하세요. diff --git a/docs/ko/runner-isolation.md b/docs/ko/runner-isolation.md index d7dea8d..7aa5afc 100644 --- a/docs/ko/runner-isolation.md +++ b/docs/ko/runner-isolation.md @@ -1,91 +1,51 @@ + + # 러너 격리 [English](../runner-isolation.md) | [한국어](runner-isolation.md) -**목표** 실행 격리 OS는 Linux 컨테이너입니다. **현재** `exec_command`는 -호스트 프로세스입니다. Linux 헬퍼 probe가 성공하면 pipe와 PTY spawn이 -같은 `codespace-linux-sandbox run --plan` argv를 씁니다(bubblewrap + -`no_new_privs`/seccomp; Codex 변환은 그 프로세스 안). prepare / -protocol / helper OS spawn 실패는 `PROCESS_SPAWN_FAILED`입니다. -managed helper가 spawn된 뒤 `run --plan` load, Restricted self-exec, -Enabled 프록시 spawn, inner sandbox 실패는 managed process exit입니다. -probe가 실패하면(macOS, bwrap 없음) Restricted는 샌드박스 -없이 실행하고 `workspace_info`는 `none`을 광고합니다. -Enabled는 헬퍼 없이 `PROCESS_SPAWN_FAILED`이며 호스트 네트워크가 -아닙니다. -게이트웨이 단위 시험은 macOS에서 실행할 수 있습니다. 그것은 -개발 노트북에서 Linux 격리를 검증했다는 주장이 아닙니다. - -## compose 픽스처가 하는 일 - -[`deploy/compose.yml`](../../deploy/compose.yml)은 **격리 픽스처**입니다. -비특권 사용자(`uid 10001`)로 실행하고, 워크스페이스만 `/workspace`에 -바인드 마운트한 뒤 sleep합니다. `codespace-mcp` / `codespace-patch`를 -실어 보내지 않으며 `exec_command`에 **연결되어 있지 않습니다**. - -마운트하지 않는 것: - -- host `$HOME` -- SSH agent socket -- `/var/run/docker.sock` -- gateway `.env`, Bearer files, or SQLite - -지금은 compose 픽스처에 러너 제어 소켓이 없습니다. 이 픽스처에 호스트 -Docker 소켓이나 이후 제어 소켓을 실수로 추가하지 마세요. 선택적 -`CODESPACE_RUNNER=uds`는 이 픽스처 밖의 **비공개** 게이트웨이↔워커 -Unix 소켓을 씁니다. 게이트웨이가 unique 0700 leaf를 만듭니다 -(`$TMPDIR/codespace-runner--/` 또는 -`$CODESPACE_RUNNER_DIR/run--/`). 소켓은 항상 -`$dir/runner.sock`입니다. 살아있는 소켓은 `connect`로 조사하며 -`ConnectionRefused` leftover만 unlink합니다. `/tmp` 자체는 chmod하지 -않습니다. - -## macOS / Docker 없음 - -`codespace-runner::PathSandbox`는 단위 시험과 `read` / `find` / versions에 -같은 상대 경로 + 심링크 + 특수 파일 규칙을 적용합니다. 워크스페이스 -인가이지, 레이스에 안전한 I/O가 아닙니다. 파일 바이트, metadata, mkdir, -chmod, remove, 제한된 walk는 격리된 `crates/file-system`(`codespace-fs`, -no-follow `LOCAL_FS`)을 탑니다. PathSandbox의 lstat과 어댑터 open 사이에 -살아 있는 프로세스가 트리를 바꿀 수 있습니다. 안전 경계는 no-follow -I/O입니다. Docker를 쓰지 -않으면 **Linux 컨테이너 격리는 검증되지 않습니다**. 호스트 -seccomp/AppArmor와 Docker Desktop 대 Linux 엔진 차이도 검증되지 않습니다. - -## 이후 프로세스 분리 - -오늘은 기본으로 `codespace-mcp`가 한 프로세스입니다. `crates/runner`가 -`InProcessRunner`(`PathSandbox`, `apply_patch` 트랜잭션 하나, 호스트 -감독)와 선택적 Unix 소켓 `UdsRunner` 클라이언트를 호스팅합니다. -워커는 격리된 `crates/codex-runtime`(`codespace-codex-runtime`)입니다. -`codex_process_hardening::pre_main_hardening()`은 `main`의 첫 줄로 -유지합니다(워커/헬퍼 **프로세스** 강화이지 command sandbox가 아닙니다. -`ctor` 없음). 그다음 `$dir/runner.sock`에 bind만 합니다(부모 chmod -없음). 프로세스당 `InProcessRunner`는 **하나**입니다. 와이어는 **u32 -length-prefix + CodeSpace JSON**입니다 (`protocol: 3`, Hello 핸드셰이크, -`request_id` `rrpc-…`, 이벤트에 `ProcessExited`). App Server가 아닙니다. -P0 UDS는 1:1입니다. 게이트웨이가 워커 자식을 소유합니다(`kill_on_drop`). -연결 끊김이나 게이트웨이 종료는 워커와 호스트 자식을 죽입니다. -`process_id`는 살아남지 않으며 재연결은 없습니다. 러너 `Replay`는 같은 -연결에서만 동작합니다. 그 **전송**은 구현되어 있으며 선택적입니다 -(`CODESPACE_RUNNER=uds` / `CODESPACE_RUNTIME_BIN`). **같은 호스트**이며 -Linux 격리를 주장하지 않습니다. Linux command sandbox는 그 같은 -`InProcessRunner` spawn(`helper probe` / `prepare` / `run --plan`)이며 -두 번째 전송 재작성이 아닙니다. 소켓 -프리미티브로 `codex-uds`를 선호하세요. Runner RPC는 CodeSpace 계약으로 -남습니다. - -Linux 격리는 여전히 목표 OS입니다. Landlock, seccomp, PTY 헬퍼, UDS, -파일시스템 역학, 네트워크 격리는 **기본 자체 스택이 아닙니다**. 업스트림 -실행 서브그래프를 선호하세요 -([codex-reuse.md](codex-reuse.md)). 단계: -process-hardening → PTY → UDS/path → filesystem → linux-sandbox → -network (filesystem은 `crates/file-system`, linux-sandbox는 -`crates/linux-sandbox` 바이너리와 `crates/linux-sandbox-protocol`). -`codex-linux-sandbox`는 컨테이너 옆에 둘 수 -있습니다. 그 `codex-core` **dev-dep**는 제품 그래프에서 빼 두세요. -P0 network(`Enabled` + 관리 프록시)는 가져왔습니다. `codex-exec`는 -거절된 채로 남습니다. `codex-exec-server`는 참고 / 이후 백엔드이며 -영구 거절은 아닙니다. 게이트웨이 정책이 유일한 허용 경로입니다. compose -픽스처에 호스트 Docker 소켓이나 이후 제어 소켓을 실수로 마운트하지 -마세요. +실행을 담당하는 프로세스, 명령에 적용하는 샌드박스, 컨테이너로 실행을 옮기는 기능은 서로 다릅니다. 현재 앞의 두 가지는 구현되어 있으며 컨테이너 실행은 아직 연결되지 않았습니다. + + + + +## 호스트와 worker 실행 + +`in-process`는 `codespace-mcp` 안에서 프로세스를 관리합니다. `uds`(Unix domain socket, Unix 도메인 소켓)는 같은 호스트의 `codespace-codex-runtime` 안에서 같은 관리 코드를 실행합니다. worker는 시작 시 Codex의 프로세스 보호 설정을 적용하고 전용 Unix 소켓을 엽니다. worker 자체를 보호하는 것과 실행할 명령에 샌드박스를 적용하는 것은 별개입니다. + +게이트웨이는 임시 디렉터리 또는 `CODESPACE_RUNNER_DIR` 아래에 권한 0700의 고유 디렉터리를 만듭니다. 내부 통신은 u32 길이 접두부, 버전 3 핸드셰이크, 요청 ID, 프로세스 종료 이벤트를 사용하는 CodeSpace JSON입니다. Codex App Server RPC가 아닙니다. 같은 연결에서의 요청 재처리는 재접속 복구를 뜻하지 않습니다. 게이트웨이와 worker 연결이 끊기면 관리 중인 worker와 자식 프로세스가 종료되고 핸들이 사라집니다. + + + + +## Linux 명령 샌드박스 + +파이프와 PTY 실행 모두 같은 Linux 도우미 경로를 사용합니다. Linux에서 `CODESPACE_LINUX_SANDBOX_BIN` 또는 실행 파일 옆의 `codespace-linux-sandbox`를 검사하고, 성공하면 샌드박스 준비·실행을 사용합니다. 검사 결과는 해당 프로세스에서 캐시됩니다. + +```text +Runner → 도우미 prepare (CodeSpace JSON, 프로토콜 1) + ← 실행 계획 파일 경로 +Runner → 관리 프로세스로 도우미 run --plan 실행 + → Codex 샌드박스 설정 → 요청한 명령 +``` + +Codex 권한 변환과 샌드박스 인자는 실행 파일 전용 도우미 안에서 처리합니다. 실행 계획은 권한 0600의 비공개 파일이며 도우미가 읽어 실행에 사용합니다. Runner는 작은 프로토콜 crate에만 의존하며 샌드박스 구현 라이브러리를 직접 가져오지 않습니다. restricted 실행은 도우미 프로세스가 자신을 실행 명령으로 교체하며, enabled 실행은 도우미가 프록시를 유지하면서 샌드박스 자식 프로세스를 기다립니다. + +최초 검사가 실패하면 `restricted`는 비격리 호스트 실행을 허용하며, 실행 정보에 `command_sandbox: none`과 OS 네트워크 집행 없음이 표시됩니다. `enabled`는 도우미가 없으면 실패합니다. 최초 검사가 성공한 뒤 준비·프로토콜·시작 오류가 발생하면 비격리 실행으로 대체하지 않고 `PROCESS_SPAWN_FAILED`를 반환합니다. 이미 시작된 도우미 내부의 실패는 프로세스 종료로 관측되며 현재 공개 MCP 결과에는 종료 코드가 없습니다. + +## 네트워크와 파일 접근 범위 + +restricted 모드는 네트워크 네임스페이스 분리와 seccomp 제한을 사용합니다. enabled 모드는 격리된 네트워크 네임스페이스와 관리 HTTP 프록시를 사용합니다. 프록시 우회 환경변수를 비워 루프백 HTTP도 프록시를 거치게 합니다. 호스트 네트워크에 직접 연결하는 방식으로 대체 실행하지 않습니다. 목적지 도메인별 제한이나 모든 네트워크 클라이언트의 호환성을 보장하는 기능은 아닙니다. + +실제 조건은 `workspace_info.execution`에서 확인하세요. 파일 도구의 작업 공간 범위는 명령 샌드박스와 별도로 적용됩니다. 논리적 경로 검사 뒤에서 `codespace-fs`가 no-follow I/O를 수행합니다. 패치 도우미에는 별도의 경로 검사와 Codex 패치 옵션이 있으며, 명령 샌드박스가 모든 파일 도구를 자동으로 감싸지는 않습니다. + +샌드박스 명령은 사용자 홈의 도구 체인을 마운트하지 않고 제한된 시스템 PATH를 사용합니다. 필요한 컴파일러, 패키지 캐시, 실행 파일을 실행 환경에 준비하세요. 호스트에서 동작하는 명령도 샌드박스 안에서는 실행 파일이나 의존성을 찾지 못할 수 있습니다. + + + + +## 컨테이너 실험 구성과 검증 + +`deploy/compose.yml`은 선택한 작업 공간만 `/workspace`에 마운트하고 일반 사용자로 대기하는 컨테이너입니다. 서버를 설치하거나 Runner를 시작하지 않으며 `exec_command` 요청을 받지 않습니다. 이 구성을 시작했다고 MCP 명령 샌드박스가 활성화되는 것은 아닙니다. + +CI의 Linux 격리 작업은 bubblewrap을 설치하고 격리 테스트에서 도우미 사용 가능 여부를 필수로 확인합니다. macOS 검사는 호스트 동작을 대상으로 하며 Linux 정책 집행을 검증하지 않습니다. 커널 탈출, Docker Desktop 차이, 임의의 원격 배포 환경은 별도 평가가 필요합니다. 설치는 [운영](operations.md), 신뢰 조건은 [보안 모델](security-model.md)을 참고하세요. diff --git a/docs/ko/security-model.md b/docs/ko/security-model.md index 28f7293..6279012 100644 --- a/docs/ko/security-model.md +++ b/docs/ko/security-model.md @@ -1,137 +1,56 @@ + + # 보안 모델 [English](../security-model.md) | [한국어](security-model.md) -CodeSpace는 커널 샌드박스가 아닙니다. CoS와 cokacremote도 아닙니다. -인가는 **게이트웨이 정책**입니다. Linux 컨테이너 격리가 **목표** 실행 -OS입니다. 현재 `exec_command`는 워크스페이스 cwd를 가진 호스트 -프로세스입니다. Codex 패치 크레이트는 제품 경계를 공급하지 않습니다. -Codex 세션 설정과 `permissionProfile`은 허용 경로가 아닙니다. 게이트웨이와 -러너는 둘 다 Rust입니다. 언어를 나눠도 신뢰 경계가 추가되지 않습니다. -프리미티브 대 제품: -[codex-reuse.md](codex-reuse.md). 실행 전용 기반: -[execution-substrate.md](execution-substrate.md). +CodeSpace는 개인용 단일 사용자 배포를 기준으로 설계되어 있습니다. 외부 에이전트가 사용할 디렉터리는 운영자가 정합니다. 파일 도구 권한, 명령 격리, 네트워크 정책 집행은 서로 다른 제어입니다. 도구 이름만으로 안전성을 판단하지 말고 실제 작업 공간의 실행 정보를 확인하세요. + + + + + + ## 신뢰 경계 -1. **MCP client** — 인가에 대해 신뢰하지 않음. `workspace_id`, - `approved: true`, 절대 경로를 포함해 어떤 도구 인자도 보낼 수 - 있습니다. 그 인자는 권리를 부여하지 않습니다. -2. **Gateway (`crates/server` + `crates/policy`)** — 전송을 인증하고 - (HTTP 실험의 선택적 정적 Bearer), 등록된 워크스페이스를 고르며, - 프로필이 허용하지 않는 작업을 거절하도록 신뢰합니다. -3. **Runner (`crates/runner`, 오늘은 프로세스 내부)** — 경로 정책 - (`PathSandbox`)을 강제하고 이미 인가된 동작의 **호스트** 프로세스를 - 감독하도록 신뢰합니다. 게이트웨이 비밀을 보도록 신뢰하지 않습니다. - 이후 Unix 소켓 / 컨테이너 분리는 같은 Rust 워크스페이스를 유지합니다. - 프로세스 경계이지 언어 경계가 아닙니다. `deploy/` 아래 compose는 - 격리 픽스처이지 이 프로세스가 아닙니다. -4. **Patch helper (`codespace-patch` + `crates/patch`)** — 헬퍼 자식 - **안에서 프로세스 내부로** Codex V4A를 파싱/검증/적용하도록 - 신뢰합니다. 게이트웨이는 JSON stdin/stdout으로 그 자식과 대화합니다. - 샌드박스로는 신뢰하지 않습니다(업스트림 독립 apply는 sandbox `None`을 - 쓰고 심링크를 따를 수 있음). 폐기된 `native/patch-worker`가 아닙니다. - -## 인증 대 선택 - -- 선택적 정적 Bearer는 **HTTP 실험 전용**입니다. OAuth 서버가 아닙니다. - 토큰은 로그나 오류 페이로드에 절대 나타나면 안 됩니다. -- `workspace_id`는 선택자입니다. id를 아는 것이 인증이 아닙니다. -- `work_id`와 `intent_id`는 선택자입니다. 아는 것이 인증이 아닙니다. -- 사용자 의도 본문은 지시입니다. 워크스페이스 프로필을 올리거나 경로 - 정책을 우회하지 않습니다. -- ChatGPT 대화 id는 신뢰 기반이 아닙니다. - -## 워크스페이스 레지스트리 - -워크스페이스는 모델이 아니라 **서버 설정**에 등록됩니다. - -각 항목은 `workspace_id` → `{ root, profile }`을 매핑합니다. - -알 수 없는 id는 거절됩니다. 경로는 엔진에 넘기기 **전에** 해석됩니다. -상대 경로만. 해석 후 그 워크스페이스 루트 안에 남아 있어야 합니다. - -## 프로필 (MVP) - -| 프로필 | 의미 | -| --- | --- | -| `read-only` | Default. `read` / `find` / `workspace_info` / `operation_status`. No patch, no shell. | -| `workspace-write` | Explicit. Mutating patch and shell **inside** the workspace. A live shell can delete workspace files; the product says so honestly. | -| `host-admin` | **Excluded from MVP.** | - -바쁜 셸은 워크스페이스 쓰기 잠금을 잡습니다. 다른 변경 작업은 기다리거나 -`WORKSPACE_BUSY`로 실패합니다. - -## 제품 경로 정책 (크레이트가 허용해도 항상) - -- 상대 경로만. -- 심링크 대상과 특수 파일(디바이스, 소켓, fifo)을 거절. -- 목적지가 이미 있으면 Add File을 거절. -- 목적지가 이미 있으면 Move를 거절. -- 워크스페이스 밖으로 `..`를 따르지 않음. -- 모델의 호스트 절대 경로를 엔진에 넘기지 않음. -- `PathSandbox`는 논리 경로를 인가합니다. 이후 open을 안전하게 만들지 - **않습니다**. 살아 있는 `read` / `find`는 디렉터리를 심링크로 바꾸는 - 프로세스와 경쟁할 수 있습니다(TOCTOU). -- Runner 소유 파일 연산과 rollback은 `codespace-fs`를 씁니다. Codex - `LOCAL_FS` 위의 얇은 어댑터이며 `follow_symlinks: false`입니다. -- `apply_patch` mutation은 `crates/patch` → - `apply_patch_with_options` + `LOCAL_FS` `follow_symlinks: false` - (`sandbox: None`)입니다. 헬퍼 프리플라이트와 사후 hash는 아직 - `std::fs`를 쓸 수 있으며, 그 경로는 no-follow 경계가 아닙니다. -- 공통 primitive는 `codespace-fs` 자체가 아니라 Codex `LOCAL_FS` - no-follow입니다. `sandbox: None`은 그 고정을 끄지 않습니다. - -[behavior-differences.md](behavior-differences.md)를 보세요. - -## 러너 격리 (Linux) - -**현재:** `exec_command`는 argv + 워크스페이스 cwd + `env_clear`로 -호스트에서 실행됩니다. 경로 샌드박스는 `read` / `find` / versions / -rollback에 적용되며 Linux 네임스페이스가 아닙니다. - -**목표 / 픽스처:** 비특권 컨테이너 사용자. 워크스페이스를 `/workspace` -(또는 동등한 전용 볼륨)에 마운트합니다. 다음을 마운트하지 **마세요**. - -- host home -- SSH agent socket -- `/var/run/docker.sock` -- gateway `.env`, Bearer files, SQLite - -[`deploy/compose.yml`](../../deploy/compose.yml)은 `sleep infinity`로 그 -속성을 보여줍니다. `exec_command`에 연결되어 있지 않습니다. 지금은 러너 -제어 소켓이 없습니다. - -게이트웨이 단위 시험은 macOS에서 실행할 수 있습니다. 개발 노트북에서 -Linux 격리를 검증했다는 주장이 아닙니다. - -## 패치 정직성 - -상태: `applied`, `checked`, `rejected`, `failed_rolled_back`, -`failed_partial`, `unknown`. - -- 성공한 `check_only` 미리보기 → 파일 변경 없음 (`checked`). -- 프리플라이트 / 정책 실패 → 파일 변경 없음 (`rejected`). -- 스냅샷을 복원한 적용 실패 → `failed_rolled_back`. -- 남은 드리프트가 있는 적용 실패 → `failed_partial` 또는 `unknown`. -- 디스크 해시가 헬퍼가 주장한 `after_version`과 일치하지 않으면 - `applied`를 보고하지 마세요(삭제는 없어야 함). -- `git reset --hard`를 쓰지 마세요. -- HTTP 타임아웃을 롤백이나 성공으로 취급하지 마세요. - -`Store::begin`이 `operation_id`를 발급한 뒤, 실행 오류는 그 id를 -`ErrorBody`에 포함합니다. 전송과 `begin` 전 거절은 포함하지 않습니다. - -## 프로세스 정직성 - -`process_id` 값은 서버가 발급합니다. 클라이언트가 핸들을 만들어 낼 수 -없습니다. 출력은 커서로 읽고 제한됩니다(프로세스당 256 KiB). 시간, 살아있는 -프로세스 수, **완료 핸들 보존**(15분 또는 완료 슬롯 64개)이 제한됩니다. -연결 끊김이 프로세스가 죽었다는 뜻은 아닙니다. 프로세스 상태는 휘발성입니다. -SQLite에 저장되지 않습니다. +게이트웨이는 인증, 작업 공간 등록, 권한 결정을 담당합니다. `workspace_id`, `work_id`, 지시문이나 `approved: true` 같은 클라이언트 입력은 권한을 부여하지 않습니다. HTTP에서는 선택적으로 정적 Bearer 토큰을 검사하지만 OAuth나 사용자별 권한 시스템은 아닙니다. + +Runner는 허용된 요청을 실행합니다. `PathSandbox`는 논리적인 파일 접근 범위를 검사하고, `codespace-fs`는 Runner 파일 작업에서 심볼릭 링크를 따라가지 않는 I/O를 수행합니다. 사전 검사만으로는 다른 프로세스가 그 직후 경로를 바꾸는 상황을 막을 수 없습니다. 패치 도우미는 별도로 CodeSpace 경로 정책과 Codex의 no-follow 패치 옵션을 적용합니다. 다만 도우미의 사전 검증과 적용 후 해시 계산에는 직접 파일 시스템을 호출하는 코드도 남아 있습니다. 모든 파일 처리가 하나의 경쟁 조건 방지 구현으로 통합되었다고 볼 수는 없습니다. + +선택적 UDS worker는 같은 호스트에서 프로세스를 분리합니다. 소켓을 전용 디렉터리에 두지만 프로세스 분리 자체가 명령 샌드박스는 아닙니다. Linux 도우미의 사용 가능 검사가 성공하면 명령 샌드박스를 사용합니다. 조건과 실패 시 동작은 [러너 격리](runner-isolation.md)에 설명합니다. + + + + + + + + +## 권한과 격리 + +`read-only`는 읽기를 허용하고 패치·명령 실행을 거부합니다. `workspace-write`는 둘 다 허용하므로 임의의 명령이 작업 공간 파일을 변경하거나 삭제할 수 있습니다. MCP 파일 도구의 상대 경로 제한만으로 호스트 명령의 접근 범위까지 제한되지는 않습니다. + +Linux 도우미가 없으면 restricted 정책의 작업 공간에서도 비격리 호스트 명령을 실행할 수 있으며 `network.enforcement`는 `none`입니다. 네트워크가 차단된다는 보장이 아닙니다. enabled 정책은 도우미가 필요하며 호스트 네트워크로 대체 실행하지 않습니다. 현재 enabled 프록시는 모든 목적지 도메인을 허용하므로 도메인별 허용 목록 기능은 아닙니다. + +운영자가 등록한 루트 경로는 신뢰의 기준입니다. 토큰, 작업 데이터베이스, 게이트웨이 설정과 비밀 정보는 관리 대상 루트 밖에 두세요. 파일 도구가 상대 경로를 검사한다는 이유로 높은 권한의 호스트, Docker 소켓, SSH 에이전트를 작업 공간에 노출해서는 안 됩니다. + + + + + + +## 작업과 복구의 안전성 + +버전 검사는 예상하지 못한 파일 버전에 패치를 적용하는 것을 막습니다. 작업 키는 패치 중복 요청을 구분하며 인증 토큰이 아닙니다. 작업 공간 점유는 파일을 변경할 수 있는 패치·명령 실행을 직렬화합니다. 큐 스케줄러와 영속적인 프로세스 복구는 없습니다. + +패치 스냅샷 복원은 가능한 범위에서 수행하며 모든 실패의 롤백을 보장하지 않습니다. `unknown`, 부분 실패, 적용 후 검증 오류가 발생하면 해당 파일을 확인하세요. [패치 동작](behavior-differences.md)과 [연동 복구 규칙](agent-integration.md)을 참고하세요. + + + + +## 검증 근거와 한계 -## 로깅 +보안 테스트에는 경로 이탈, 특수 파일, 인증 응답, 작업 재조회, 동시 작업 사례가 있습니다. Linux 도우미 테스트는 샌드박스와 프록시 동작을 검사합니다. 테스트 범위가 커널 탈출 감사나 모든 배포 환경의 안전성을 뜻하지는 않습니다. [검증 범위 목록](../../tests/adversarial-report.md)을 참고하세요. -Authorization 헤더, Bearer 토큰, `.env` 값을 가리세요. 원본 요청을 -덤프하기보다 구조화 필드(`workspace_id`, `operation_id`)를 선호하세요. -별도 감사 서브시스템은 없습니다. +구조화된 stderr 로그에는 비밀 정보 가림 처리가 있지만 원시 도구 요청이나 비밀을 로그에 남기지 않도록 해야 합니다. 별도의 위변조 방지 감사 서비스는 없습니다. 로컬 MCP 테스트만으로 공개 HTTPS·ChatGPT 배포나 다중 사용자 권한을 검증할 수는 없습니다. diff --git a/docs/ko/upstream-lock.md b/docs/ko/upstream-lock.md index 5d99687..380b6cb 100644 --- a/docs/ko/upstream-lock.md +++ b/docs/ko/upstream-lock.md @@ -1,109 +1,47 @@ -# 업스트림 고정 + + + +# Codex 고정 버전 [English](../upstream-lock.md) | [한국어](upstream-lock.md) -CodeSpace는 OpenAI Codex를 **핀된** git 서브모듈로 재사용합니다. 단일 -소스 파일을 벤더하지 않고, 독립 `apply_patch` 바이너리를 보안 경계로 -감싸지 않으며, `main`을 따라가지 않습니다. +현재 Codex 실행 어댑터는 모두 `third_party/codex`의 Git 서브모듈을 사용합니다. 커밋을 고정하여 공통 구현을 재현할 수 있게 하며 업스트림의 새 릴리스가 자동으로 반영되지는 않습니다. -**현재 코드 재사용**은 `crates/patch`를 통한 `codex-apply-patch`(V4A의 -파싱 / 검증 / 적용), 그리고 `codex-process-hardening`(`codespace-patch`와 -`codespace-codex-runtime`)과 `codex-uds`(런타임 워커 bind)입니다. -그 핀은 실행 구현 공급이며, 그만큼 좁은 크레이트만 따라온다는 서약이 -아닙니다. 제품 런타임(App Server, `codex-core`, `codex-exec`, login, -models)은 **핵심** 밖에 둡니다. 응집력 있는 **실행 서브그래프**는 -격리된 어댑터에서 가져올 수 있습니다 -([codex-reuse.md](codex-reuse.md)). Codex 타입은 `crates/domain`이나 -MCP 표면으로 새면 안 됩니다. 실행 전용 규칙: -[execution-substrate.md](execution-substrate.md). + + -## 배포 핀 (W06) +## 배포 기준 버전 -| 필드 | 값 | +| 항목 | 값 | | --- | --- | -| Project | [openai/codex](https://github.com/openai/codex) | -| License | Apache-2.0 (see root `NOTICE`) | -| Tag | `rust-v0.154.0` | -| Commit | `6b9826e3aa83b1a5947db50f4332cb9c65f1b340` | -| Path | `third_party/codex` git submodule | -| Crate | `codex-apply-patch` via Cargo path dependency from `crates/patch` | -| Apply options | `PreserveLineEndings`, `follow_symlinks: false` | -| Parity | subset in `tests/parity/` and `crates/patch` tests; not the full upstream suite | +| 프로젝트 | [OpenAI Codex](https://github.com/openai/codex) | +| 라이선스 | Apache-2.0. 출처는 [NOTICE](../../NOTICE)에 기록 | +| 태그 | `rust-v0.154.0` | +| 커밋 | `6b9826e3aa83b1a5947db50f4332cb9c65f1b340` | +| 경로 | `third_party/codex` | +| 사용하는 구성 요소 | 패치, 런타임 worker, PTY, 파일 시스템, Linux 샌드박스·프록시 어댑터 | +| 패치 옵션 | `PreserveLineEndings`, `follow_symlinks: false` | -`crates/patch`는 **격리된 Cargo 워크스페이스**입니다(저장소 루트 -워크스페이스에서 제외). Codex 크레이트가 자체 `workspace.dependencies`를 -유지하게 합니다. 그 `Cargo.lock`은 핀된 Codex lockfile에서 시작해 -전이 크레이트(예를 들어 맞는 `rama-*` 알파)가 떠다니지 않게 합니다. -Codex `[patch.crates-io]` git 포크는 `crates/patch/Cargo.toml`에 -복사됩니다. +정확한 역할은 [연결된 구성 요소](codex-reuse.md)를 참고하세요. 패치 테스트는 선택된 동작 일치 사례를 검사하며 업스트림 전체 테스트나 Codex의 모든 동작을 검증하지는 않습니다. -어댑터는 `parse_patch`, 그다음 제품 경로 정책(심링크 조상 거절 포함), -그다음 같은 프로세스에서 `LOCAL_FS`로 `apply_patch_with_options`를 -호출합니다. `git apply`나 독립 `apply_patch` 바이너리를 호출하지 -않습니다. 라이브러리에 `sandbox: None`을 넘기는 것은 제품 샌드박스가 -**아닙니다**. 정책 + no-follow I/O + Linux 러너입니다. + + + + -macOS에서 `/var`는 `/private/var`의 심링크입니다. 어댑터는 `PathUri` -cwd를 만들기 전에 워크스페이스 루트를 정규화해, no-follow 탐색이 그 -호스트 별칭에서 실패하지 않게 합니다. +## Cargo workspace와 잠금 파일 -## 파일 복사 벤더가 금지인 이유 +패치 파서를 복사하여 별도 구현으로 유지하지 않고 업스트림의 workspace 의존성을 사용할 수 있도록 어댑터를 별도 Cargo workspace로 구성합니다. 어댑터 잠금 파일과 필요한 업스트림 Cargo 패치를 유지하세요. 파일 시스템·샌드박스 어댑터는 호환되지 않는 alpha·stable 혼합을 피하기 위해 일치하는 Rama alpha 의존성을 고정합니다. -`codex-apply-patch` 0.154.0은 워크스페이스 크레이트입니다. 같은 저장소의 -다른 크레이트에 의존합니다. 포함: +패치 어댑터는 `codespace-patch` 내부에서 라이브러리를 호출하며 업스트림의 독립 `apply_patch` 실행 파일을 호출하지 않습니다. `LOCAL_FS`와 경로 도구는 구현 의존성입니다. Codex 사용자 설정은 작업 공간 접근 권한을 부여하지 않습니다. -- `codex-exec-server` -- `codex-utils-absolute-path` -- `codex-utils-path-uri` -- tree-sitter 관련 워크스페이스 크레이트 + + -`apply-patch` 소스를 `crates/patch`에 복사하면 빌드가 실패하거나 엔진을 -조용히 포크합니다. 그래서 CodeSpace는 다음을 사용합니다. +## 확인과 갱신 -```text -git submodule add https://github.com/openai/codex.git third_party/codex -git -C third_party/codex checkout 6b9826e3aa83b1a5947db50f4332cb9c65f1b340 +```bash +PIN_ONLY=1 ./scripts/check-upstream-pin.sh ``` -경로 의존성이며 crates.io의 움직이는 버전이 아닙니다. - -## 재사용하는 것과 거절하는 것 - -**이 핀에서, 코드로:** 파싱, 헝크 검증, 적용 API, 그리고 패리티용으로 -고른 업스트림 픽스처(`crates/patch` → `codex-apply-patch`); -`pre_main_hardening()`(`codex-process-hardening`); 비공개 소켓 디렉터리 + -bind(`codex-uds`); 대화형 spawn(`crates/pty` → `codex-utils-pty`); -no-follow 파일시스템 I/O(`crates/file-system` → `LOCAL_FS`). - -**그 크레이트가 허용해도 제품 기본값으로 거절:** 심링크 follow, -sandbox `None` 독립 CLI, 모델의 호스트 절대 경로, 조용한 `git apply`. - -**CodeSpace *핵심* 의존성으로 거절:** `codex-protocol` 타입을 포함한 -어떤 Codex 크레이트 경로 의존성. 제품 런타임은 어디에나 빼 둡니다. -App Server, `codex-core`, `codex-exec`, login, models. -[codex-reuse.md](codex-reuse.md)를 보세요. 이 SHA에서 재사용 선호 -(**아직 연결 안 함**): `codex-file-search`. 적극 평가: -`codex-shell-command`, `codex-linux-sandbox` -(전이 `codex-sandboxing`, `codex-network-proxy`; `codex-protocol`은 -어댑터에서만 허용). `codex-exec-server-protocol`은 내부 DTO 후보입니다. -`codex-exec-server`는 참고 / 이후 백엔드이며 영구 거절은 아닙니다. -`codex-git-utils` / `codex-worktree`는 연결하지 않습니다. - -`codex-rs` 독립 `apply_patch`를 감싸고 그것을 샌드박스라고 부르지 -마세요. Preview / `check_only`는 라이브러리 파싱과 CodeSpace -프리플라이트로 구현하며, `apply_patch --check`가 있다고 가정하지 -않습니다. - -Codex 세션 `permissionProfile`을 허용 경로로 취급하지 마세요. -게이트웨이 정책이 유일한 인가 권한입니다. - -## 승격 규칙 - -1. 후보를 기록합니다 (W01). -2. W06: 서브모듈 + 어댑터 + 패리티 부분집합 (이 핀). -3. 패리티가 실패하면 **출하하지 마세요**. 어댑터 옵션을 바꾸거나 다른 - 리비전을 고르세요. 불일치를 덮지 마세요. -4. W13: [upstream-update.md](upstream-update.md)를 따르세요. 배포 - 단계로 최신 Codex `main`에 `git submodule update --remote`를 하지 - 마세요. SHA나 패치 시험이 실패하면 `scripts/check-upstream-pin.sh`는 - 빨간 상태로 남아야 합니다. +이 명령은 서브모듈 커밋과 문서의 일치 여부만 검사합니다. `PIN_ONLY`를 생략하면 패치 테스트도 실행하지만 다른 어댑터 검증을 대신하지는 않습니다. 버전을 바꾸기 전에 [전체 업데이트 절차](upstream-update.md)를 따르세요. 배포 과정에서 `git submodule update --remote`로 최신 버전을 따라가지 않습니다. diff --git a/docs/ko/upstream-update.md b/docs/ko/upstream-update.md index 923acf9..7fd2d8f 100644 --- a/docs/ko/upstream-update.md +++ b/docs/ko/upstream-update.md @@ -1,66 +1,38 @@ -# 업스트림 핀 갱신 + + + +# Codex 의존성 업데이트 [English](../upstream-update.md) | [한국어](upstream-update.md) -Codex 리비전을 바꾸는 것은 **의도적 릴리스**이며, `main`으로 -`git submodule update --remote`가 아닙니다. 패리티 부분집합이 실패하면 -**출하하지 마세요**. 워크스페이스 의존성을 덮으려고 `apply-patch` 소스를 -`crates/patch`에 복사하지 마세요. +고정 버전은 검토 가능한 PR로 변경합니다. 업데이트는 [Codex 재사용 범위](codex-reuse.md)에 있는 모든 어댑터의 프로세스·파일 시스템·네트워크 동작에 영향을 줄 수 있습니다. + + + -현재 핀: [upstream-lock.md](upstream-lock.md). -제품 대 크레이트 기본값: [behavior-differences.md](behavior-differences.md). -apply-patch 외에 재사용할 수 있는 것: [codex-reuse.md](codex-reuse.md). -NOTICE는 Apache-2.0 Codex 귀속을 유지해야 합니다. +## 후보 검토 -## 점검 목록 +태그나 커밋을 명시적으로 선택하고 이유를 기록합니다. 패치 처리, PTY, 프로세스 보호, 파일 시스템, 샌드박스, 프록시의 관련 변경을 확인하세요. 런타임 의존성과 개발용 의존성은 구분합니다. 개발용 의존성이 있다는 사실만으로 제품 실행 파일에 포함된다고 볼 수는 없습니다. -1. **태그 또는 커밋**을 고르세요(떠 있는 `main` 아님). 이유를 기록하세요. -2. `git submodule update --init third_party/codex` -3. `git -C third_party/codex fetch --tags` -4. `git -C third_party/codex checkout ` -5. 격리된 어댑터를 다시 빌드하세요: - `cargo test --manifest-path crates/patch/Cargo.toml` - `cargo clippy --manifest-path crates/patch/Cargo.toml --all-targets -- -D warnings` -6. `docs/upstream-lock.md`의 Commit 칸을 새 SHA로 갱신한 뒤 - `scripts/check-upstream-pin.sh`를 실행하세요(서브모듈 HEAD ≠ lock - 파일이면 스크립트가 실패합니다). -7. 적용 옵션, 심링크 정책, 또는 파싱 오류가 바뀌면 - [behavior-differences.md](behavior-differences.md)를 갱신하세요. -8. 재사용 설명이나 핀 문자열이 바뀌면 [NOTICE](../../NOTICE)를 갱신하세요. -9. 핀의 **실행 서브그래프**를 [codex-reuse.md](codex-reuse.md)에 비춰 - 판단하세요: 응집력 있는 실행 대 에이전트 / 모델 의미 대 Gateway 허용 - 우회. process-hardening, PTY, UDS, filesystem, linux-sandbox, - network-proxy의 diff는 실행/보안 변경 로그로 취급하세요. 후보 표를 - 갱신하세요. 루트 워크스페이스에 Codex 경로 의존성을 추가하지 마세요. - 격리는 `crates/patch`와, 생기면 `crates/codex-runtime` - (`codespace-codex-runtime`)에 남습니다. -10. 그 런타임 워크스페이스가 생기기 전까지 게이트는 SHA + 패치 패리티 - 뿐입니다. 생기면 추가로: 런타임 어댑터 컴파일, 그리고 PTY / - sandbox / process 회귀. 이 작업 패키지는 그 스위트를 추가하지 - 않습니다. -11. PR을 여세요. CI는 핀 검사 **와** `crates/patch` 시험을 실행해야 - 합니다. 빨간 패치 job은 경고가 아니라 실패한 배포입니다. +서브모듈, [버전 기록](upstream-lock.md), 영향받는 어댑터 잠금 파일, 필요한 출처 고지를 함께 갱신합니다. 핵심 타입과 Codex 어댑터 타입의 분리를 유지하세요. 호환되지 않는 의존성을 감추기 위해 업스트림 crate 하나를 제품에 복사하지 않습니다. -실패한 패리티 실행을 성공으로 표시하는 경로는 **없습니다**. + + -## 로컬 게이트 +## 제출 전 검증 -```bash -./scripts/check-upstream-pin.sh -``` +1. `PIN_ONLY=1 ./scripts/check-upstream-pin.sh`로 고정 커밋을 확인합니다. +2. 루트 workspace와 분리된 patch, codex-runtime, pty, file-system, linux-sandbox 어댑터에서 `cargo fmt --check`, `cargo clippy --locked --all-targets -- -D warnings`, 테스트를 수행합니다. +3. 패치·런타임·Linux 도우미를 빌드하고 통합 테스트가 의도한 실행 파일을 사용하게 설정합니다. workspace 통합·프로토콜 테스트를 실행합니다. +4. bubblewrap과 네임스페이스를 지원하는 Linux에서 `CODESPACE_REQUIRE_LINUX_SANDBOX=1`로 격리 테스트를 실행합니다. restricted 차단과 enabled 프록시 동작을 포함합니다. +5. [CI](../../.github/workflows/ci.yml)에 정의된 의존성 정책 검사와 Runner의 금지된 샌드박스 도우미 라이브러리 의존성을 확인합니다. +6. 기본값·오류·제약이 바뀌면 동작 문서와 두 언어를 함께 검토합니다. -서브모듈 SHA가 lock 파일과 다르거나 -`cargo test --manifest-path crates/patch/Cargo.toml`이 실패하면 -종료 코드가 0이 아닙니다. +실행 어댑터도 바뀌는 업데이트를 패치 일부 테스트만으로 검증할 수는 없습니다. 명령 결과를 후보 SHA와 연결하고 수행하지 못한 플랫폼 검사는 명시하세요. 현재 검사 목록의 실행 가능한 기준은 CI workflow입니다. -`PIN_ONLY=1 ./scripts/check-upstream-pin.sh`는 SHA만 검사합니다. -CI는 그것을 fmt/clippy **전에** 실행한 뒤, SHA를 다시 검사하지 않고 -패치 시험을 실행합니다. 로컬에서 접두 없는 스크립트는 여전히 SHA + -`crates/patch` 시험을 합니다. + + -## 금지 +## 반영과 되돌리기 -- 워크스페이스 크레이트 없이 `codex-rs/apply-patch`를 파일 복사 벤더 -- 독립 `apply_patch` 바이너리를 보안 경계로 감싸기 -- 조용한 `git apply` 폴백 -- 패치 시험이 실패하는데 출하하기 +PR에 이전·새 SHA, 동작 변경, 테스트 근거를 기록합니다. 호환성 검사가 실패한 채 병합하거나 다른 패치 엔진으로 조용히 대체하지 않습니다. 되돌릴 때는 서브모듈, 어댑터 잠금 파일, 필요한 Cargo 패치, 문서를 함께 되돌리고 영향받는 검사를 다시 수행합니다. 고정 커밋 불일치는 경고가 아닌 오류입니다. diff --git a/docs/operations.md b/docs/operations.md index 1e14fa9..1d59fbe 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -1,79 +1,49 @@ -# Operations + -[English](operations.md) | [한국어](ko/operations.md) +# Installation and operations -Reproduce a local CodeSpace from a clean clone: install, start the -gateway, then `workspace_info` → `read` → `apply_patch` → `exec_command`. -This is a **personal single-user** layout. It is not a multi-tenant SaaS -and not a full OAuth server. +[English](operations.md) | [한국어](ko/operations.md) -ChatGPT Custom Connector against a public hostname is **not verified**. -See [chatgpt-connector.md](chatgpt-connector.md). +This guide sets up a personal, single-user server on macOS or Linux. For the tool-calling sequence, continue with [Agent Loop integration](agent-integration.md). ## Install -Needs Rust 1.88+, Git, and (for the Linux isolation fixture) Docker. +Install Git and a current Rust stable toolchain, matching CI. The root manifest declares Rust 1.88, but that is not a verified minimum for the full Codex adapter graph; the pinned upstream checkout selects Rust 1.95.0. On macOS, install the Xcode command-line tools. Linux command isolation additionally needs the sandbox helper, bubblewrap, and permission to create the required namespaces. Docker is only needed for the separate [container fixture](../deploy/README.md). ```bash git clone --recurse-submodules https://github.com/novelKR/CodeSpace.git cd CodeSpace -# If you already cloned without submodules: -# git submodule update --init --recursive +# For an existing clone: +git submodule update --init --recursive +cargo build --locked -p codespace-server --bin codespace-mcp --release +cargo build --locked --manifest-path crates/patch/Cargo.toml --bin codespace-patch --release +mkdir -p dist +cp target/release/codespace-mcp dist/ +cp crates/patch/target/release/codespace-patch dist/ ``` -The Codex pin is `third_party/codex` at the commit in -[upstream-lock.md](upstream-lock.md). Do not `git submodule update --remote` -to Codex `main`. Product runtime stays out of the gateway; see -[codex-reuse.md](codex-reuse.md) and -[execution-substrate.md](execution-substrate.md). +Keep the two binaries together so the server can locate the patch helper. Alternatively, set `CODESPACE_PATCH_BIN` to its absolute path. The helper runs the pinned Codex patch library; the server alone cannot apply patches. -Build the gateway, patch helper, and Linux sandbox helper into the -**same** directory so the gateway can find them next to itself (or set -`CODESPACE_PATCH_BIN` / `CODESPACE_LINUX_SANDBOX_BIN`). -The UDS worker is optional (`CODESPACE_RUNTIME_BIN`). +For Linux command isolation, build and place this helper alongside the server: ```bash -cargo build -p codespace-server --bin codespace-mcp --release -cargo build --manifest-path crates/patch/Cargo.toml --bin codespace-patch --release -cargo build --manifest-path crates/linux-sandbox/Cargo.toml --bin codespace-linux-sandbox --release -mkdir -p dist -cp target/release/codespace-mcp dist/ -cp crates/patch/target/release/codespace-patch dist/ +cargo build --locked --manifest-path crates/linux-sandbox/Cargo.toml --bin codespace-linux-sandbox --release cp crates/linux-sandbox/target/release/codespace-linux-sandbox dist/ -# Optional Unix-socket worker (not the default exec path): -cargo build --manifest-path crates/codex-runtime/Cargo.toml --bin codespace-codex-runtime --release -cp crates/codex-runtime/target/release/codespace-codex-runtime dist/ ``` -`codespace-patch` is a host child process. It hosts the pinned Codex -crate **in-process**. It is not the upstream standalone `apply_patch` -binary and not the retired `native/patch-worker`. `codespace-codex-runtime` -binds a private Unix socket with `codex-process-hardening` and -`codex-uds`, then runs `InProcessRunner`. Hardening is **worker/helper -process** hardening (`pre_main_hardening()` as the first line of -`main`; no `ctor`), not a command sandbox. Default `exec_command` still -uses in-process host spawn. On Linux, when -`CODESPACE_LINUX_SANDBOX_BIN` (or `codespace-linux-sandbox` next to the -gateway) probes successfully, that spawn is `helper run --plan` after a -short `prepare`. Codex argv never enters the runner. -`workspace_info.execution.isolation.command_sandbox` is `linux-sandbox` -only then; otherwise `none`. Restricted network is OS-enforced in that -case (`network.enforcement=enforced`). Enabled is the same enforcement -state with a managed HTTP proxy inside the helper; without the helper -it is `PROCESS_SPAWN_FAILED`, not host FullAccess. `exec_command.tty` defaults to false (pipes). -`tty: true` attaches a PTY at 24x80. Exec DTO cwd is `WorkspaceRoot`; `PATH` / -`HOME` / `LANG` are applied inside the runner process (`TERM=xterm` for PTY). - -## Workspace registry - -Copy [workspaces.example.json](workspaces.example.json) and point `root` -at a **real directory you registered**. Models cannot add workspaces. +A successful build does not prove isolation is active. Check `workspace_info.execution.isolation.command_sandbox` after connecting. See [activation and failure conditions](runner-isolation.md). + + + +## Register a workspace + +Create an existing project directory and a registry outside it. Replace `/absolute/path/to/project` with that directory; do not use this placeholder literally. ```json { "workspaces": { "demo": { - "root": "/absolute/path/to/your/project", + "root": "/absolute/path/to/project", "profile": "workspace-write", "network": "restricted" } @@ -81,148 +51,92 @@ at a **real directory you registered**. Models cannot add workspaces. } ``` -Profiles: `read-only` (default intent) or `workspace-write`. `host-admin` -is not a product profile. Optional operator `network` is `restricted` -(default) or `enabled` — this is workspace JSON like `environment`, not -a tool argument and not `{ "network": true }`. Enabled requires the -Linux helper. Optional operator `environments` may register -`host` or `linux-container`. Omitted environment is implicit local host. -`linux-container` is not an exec path. Tools and `workspace_info` have -no `environment_id`. Call `workspace_info` with a `workspace_id` to read -the effective execution contract (`execution`): policy vs backend support, -`files.*.available` and `process.available` (permission and backend -support only; not occupancy — `exec_command` or `apply_patch` may still -return `WORKSPACE_BUSY`; tool existence is `tools_exposed`), fixed 24x80 -PTY without resize when a process is available, mutation lease / -`WORKSPACE_BUSY`, workspace-scoped file tools vs Linux command sandbox -when advertised, and `network.policy` (`restricted` or `enabled`) with -OS enforcement when the helper probe succeeds. `output_combined=true` means `read_process` exposes one -combined stream; stdout/stderr identity is not preserved. `exec_command` -returns `dispatch_status` -(`confirmed` or `unknown`). Treat `unknown` patch/exec as possibly -executed; do not retry the same mutation under a new `operation_key`. - -## Run the gateway - -stdio (Cursor / local MCP host): +Save this as `workspaces.json` in the CodeSpace checkout. `read-only` permits reads; `workspace-write` also permits patches and commands. Only the operator registers roots and chooses permissions. A tool's `workspace_id` selects a registration; it grants no permission by itself. + +`network` defaults to `restricted`. `enabled` requires the Linux helper and routes supported HTTP traffic through its managed proxy; it does not grant unrestricted host networking. Without a working helper, `enabled` execution fails. With `restricted` and no helper, host execution is possible but network restrictions are not OS-enforced. Use the reported enforcement state when deciding whether an environment is suitable. + + + +## Start the server + +From the CodeSpace checkout, configure absolute paths and persistent patch/coordination storage: ```bash -export CODESPACE_CONFIG="$PWD/docs/workspaces.example.json" -export CODESPACE_OPERATIONS_DB="$PWD/data/operations.sqlite" +export CODESPACE_CONFIG="$PWD/workspaces.json" mkdir -p data -./dist/codespace-mcp +export CODESPACE_OPERATIONS_DB="$PWD/data/operations.sqlite" +export CODESPACE_PATCH_BIN="$PWD/dist/codespace-patch" +# Use a suitable operator-selected limit for builds; the default is 30 seconds. +export CODESPACE_PROCESS_TIMEOUT_SECS=300 ``` -Streamable HTTP (optional static Bearer — experiment only, never log it): +For stdio, configure your MCP client to launch the absolute path to `dist/codespace-mcp` with those environment variables. The server defaults to stdio. Running `./dist/codespace-mcp` in a terminal waits for MCP input; it does not open an interactive chat. Logs go to stderr, leaving stdout for MCP. + +For Streamable HTTP, keep the process running: ```bash export CODESPACE_HTTP_HOST=127.0.0.1 export CODESPACE_HTTP_PORT=8787 -# export CODESPACE_HTTP_TOKEN="replace-me" -export CODESPACE_CONFIG="$PWD/docs/workspaces.example.json" -export CODESPACE_OPERATIONS_DB="$PWD/data/operations.sqlite" +# Set CODESPACE_HTTP_TOKEN securely if your client will send a Bearer token. ./dist/codespace-mcp --http ``` -Endpoint: `http://127.0.0.1:8787/mcp`. User Inbox JSON is -`http://127.0.0.1:8787/inbox` on the **same** HTTP listener and Bearer. -stdio-only mode does not expose `/inbox`. Drafts stay off the model -until `POST /inbox/intents/{id}/queue`. Bind address is not the same as a -public `Host` header. For a reverse proxy, allow the external hostname -separately; do not treat `0.0.0.0` as that name. - -If `CODESPACE_OPERATIONS_DB` is unset, operations and the intent queue -live in memory and **do not survive restart**. Process handles never -survive restart. - -Opt-in runner worker (still host exec, not Linux isolation). UDS is -1:1: the gateway owns `RuntimeProcess` (child, private 0700 directory, -`$dir/runner.sock`). There is no reconnect. Allowed `--runner` / -`CODESPACE_RUNNER` values are `in-process` and `uds` only. `--runner-dir` / -`CODESPACE_RUNNER_DIR` may name a parent for that unique leaf; `/`, -`/tmp`, `/var/tmp`, and `$HOME` are rejected as the directory itself. -`--runner-socket` is only for connecting to an already-running worker -and does not chmod the parent path. +Connect an MCP client to `http://127.0.0.1:8787/mcp`. The same listener exposes `/inbox` as a JSON API with the same optional Bearer authentication. It is unavailable in stdio-only mode. The server does not automatically load `.env.example`. + +Public HTTPS and reverse-proxy deployment need separate verification. The current HTTP Host allowlist is constructed from loopback and bind-host values; there is no separate public-host configuration option. A public hostname may therefore be rejected. Do not assume that binding to `0.0.0.0` configures an external hostname. + + + +## Verify the first connection + +Complete MCP initialization, list tools, and call `workspace_info` with `{"workspace_id":"demo"}`. Confirm that file and process availability match your intended profile. Then read a known project file and follow the [small patch and process example](agent-integration.md). + +`available` combines permission and backend support, not current occupancy. A later patch or command may still return `WORKSPACE_BUSY`. A registered `linux-container` environment has no implemented file or exec backend. + +## Optional Unix-socket worker + +Build the worker and select it before starting the gateway: ```bash +cargo build --locked --manifest-path crates/codex-runtime/Cargo.toml --bin codespace-codex-runtime --release +cp crates/codex-runtime/target/release/codespace-codex-runtime dist/ export CODESPACE_RUNNER=uds -export CODESPACE_RUNNER_DIR="$PWD/data/runner" export CODESPACE_RUNTIME_BIN="$PWD/dist/codespace-codex-runtime" -./dist/codespace-mcp ``` -## Reproduce the MVP flow +The worker runs on the same host and is not a container. The gateway creates a private socket directory and owns the child. Worker connection loss or gateway shutdown ends that worker's processes; reconnect and process recovery are not supported. The default remains `in-process`. -Automated coverage (no ChatGPT account required): + + -```bash -cargo test -p codespace-server --test apply -cargo test -p codespace-server --test process -cargo test -p codespace-server --test protocol_compat -cargo test -p codespace-server --test inbox -cargo test -p codespace-server --test e2e -``` +## Logs, limits, and recovery -Those tests cover patch apply/disk confirmation and managed processes -(`exec_command` / `read_process` / `WORKSPACE_BUSY`). +| Setting or limit | Behavior | +| --- | --- | +| `RUST_LOG` | stderr tracing level; default `info` | +| `CODESPACE_OPERATIONS_DB` unset | In-memory patch operations and instruction queue; lost on restart | +| `CODESPACE_PROCESS_TIMEOUT_SECS` | Positive integer; default 30 seconds; set in the runner environment | +| `CODESPACE_MAX_PROCESSES` | Default 8 live processes across the runner; workspace occupancy still applies | +| Process output | Last 256 KiB retained; stdout/stderr combined; no explicit loss flag or exit code in MCP results | +| Completed handles | Default retention up to 15 minutes and 64 completed entries; not durable | -Manual stdio: connect an MCP client to `./dist/codespace-mcp` with the -env vars above, then call those tools against `workspace_id: "demo"` -(after the JSON `root` exists and the profile allows writes). +Store logs and the database outside the managed workspace. Rotate stderr capture yourself. Do not log Bearer tokens or commit real credentials. Deleting the database also deletes patch idempotency records. -## Linux isolation fixture +After losing a patch response, query `operation_status` with exactly one of `operation_id` or `operation_key`. An unfinished record becomes `unknown` after restart; inspect files before deciding what to do. Processes use `process_id` and cannot be recovered through `operation_status`. See [retry and recovery rules](agent-integration.md). -[`deploy/compose.yml`](../deploy/compose.yml) is a sleeper fixture. It -bind-mounts **only** the project at `/workspace` as uid `10001`. It does -not mount host `$HOME`, SSH agent sockets, `/var/run/docker.sock`, -gateway `.env`, Bearer files, or the operations SQLite file. It does not -run `codespace-mcp` and is **not** connected to `exec_command`. + + -```bash -export CODESPACE_WORKSPACE=/absolute/path/to/your/project -docker compose -f deploy/compose.yml up --build -``` +## Troubleshooting + +| Symptom | First check | +| --- | --- | +| `WORKSPACE_NOT_FOUND` | Registry path, registered ID, and process environment | +| Patch helper cannot start | Build `codespace-patch` and check its absolute path | +| `WORKSPACE_BUSY` | Finish or terminate the existing command before patching | +| Command times out | Operator timeout; do not assume a long build completed | +| Linux reports no sandbox | Helper location, bubblewrap, namespace support; see isolation guide | +| `enabled` command fails before starting | A working Linux sandbox helper is required | +| HTTP rejects the request | `/mcp` path, Bearer header, and Host validation | -The gateway still runs on the host. `exec_command` is a host process -(pipes by default; PTY when `tty` is true) with the workspace as cwd. - -## Logs - -- Gateway logs go to **stderr** (`RUST_LOG` / `tracing`, default `info`). -- Secret keys (`authorization`, `token`, `bearer`, …) are redacted in - structured sanitizers. Do not print `CODESPACE_HTTP_TOKEN` in shell - history docs you share. -- Rotate or truncate stderr capture yourself. There is no log SaaS. -- The operations SQLite file grows with **patch operation** rows plus - works/intents. Process handles and resource locks are volatile - memory. Keep the database off any future runner mount. Deleting it - forgets idempotency keys. - -## Recovery after disconnect or restart - -HTTP/JSON-RPC request id ≠ `operation_id` ≠ `process_id` ≠ `work_id`. A lost HTTP -response is not an execution failure. - -- Call `operation_status` with **exactly one** of the server-minted - `operation_id` or the client `operation_key` instead of blindly - re-running `apply_patch`. Providing both or neither is an error. -- After a crash, unfinished rows are `unknown`. The server does **not** - auto-replay them. A lost UDS `apply_patch` response is stored as - `unknown`, never a disk-contradicting `rejected`. Inspect the - workspace, then start a **new** `operation_key` if you still want the - change. -- A live `exec_command` process can outlive the MCP request. Use - `read_process` / `terminate_process` with the issued `process_id`. - Ambiguous transport keeps the process-owned lease. `ProcessExited` - from the worker calls `release_process` so `WORKSPACE_BUSY` does not - stick forever. UDS disconnect or gateway shutdown **kills the worker** - (host children die with it). Confirmed worker death releases - process-owned leases; `process_id` does not survive. Runner `Replay` - is a same-connection primitive, not disconnect recovery. After - gateway restart, old OS PIDs are not reused as CodeSpace handles. - -## What this document does not verify - -- Installing on someone else's laptop or a cloud VM from this session -- ChatGPT Custom Connector OAuth / public HTTPS `Host` headers -- Kernel escape of the Docker example +Local transport tests do not establish a live ChatGPT connection, compatibility with every package manager through the proxy, or protection against kernel/container escapes. diff --git a/docs/protocol-compatibility.md b/docs/protocol-compatibility.md index 4cafe3a..37edc41 100644 --- a/docs/protocol-compatibility.md +++ b/docs/protocol-compatibility.md @@ -1,112 +1,31 @@ -# Protocol compatibility + -[English](protocol-compatibility.md) | [한국어](ko/protocol-compatibility.md) - -CodeSpace is an execution-tools MCP server. Outer clients choose what to -do. This process never calls a model. - -## Core baseline - -| Layer | Baseline | Notes | -| --- | --- | --- | -| Protocol | **MCP 2025-11-25** | First-class. CI forces this revision on stdio and Streamable HTTP. | -| Transport | **stdio** and **Streamable HTTP `/mcp`** | Same tool contract on both. | -| HTTP floor | **2025-03-26** | First revision with Streamable HTTP. Not the CI pin. | -| Progressive enhancement | **2026-07-28** | Optional. Same tool semantics. Never required for core. | - -Required primitives for core execution: `initialize`, `tools/list`, -`tools/call`. Live tools today are `workspace_info`, `read`, `find`, -`apply_patch`, `operation_status`, `exec_command`, `write_stdin`, -`read_process`, `terminate_process`, `work_open`, `steer_status`, -`steer_claim_next`, `steer_complete`, and `work_finish`. They still use -`tools/call`. User drafts and reorder live on HTTP `/inbox`, not MCP. +# MCP protocol compatibility -## What core must not require - -These 2026-07-28 (and related) features may be negotiated later as UX or -acceleration. They are **not** part of the core contract and must not -gate `tools/call`: - -- MRTR (multi-round-trip requests) -- Tasks -- subscriptions -- `Mcp-Method` / `Mcp-Name` header routing (SEP-2243) -- 2026-07-28 stateless lifecycle as application state +[English](protocol-compatibility.md) | [한국어](ko/protocol-compatibility.md) -Auth and routing stay Bearer middleware (optional) plus `/mcp` → rmcp -tool dispatch. Dispatch reads the JSON-RPC method and tool name from the -body. It does not route on `Mcp-Name`. +Use ordinary MCP initialization, tool discovery, and `tools/call`. stdio and Streamable HTTP at `/mcp` expose the same application tools. Work and instruction queues are application state, independent of transport sessions. -`crates/domain`, `crates/policy`, `crates/patch`, `crates/store`, and `crates/runner` do -not import `ProtocolVersion` or `NegotiatedFeatures`. The server adapter -in `crates/server/src/protocol.rs` maps a negotiated revision to -enhancement flags. Handlers keep `tools/call` even when those flags are -true. Work/steer is application state (`work_id` / `intent_id`), not MCP -Tasks, MRTR, or subscriptions. + + + + + -Later **approval** or long-running **Tasks** (see -[execution-substrate.md](execution-substrate.md)) must keep a 2025-11-25 -`tools/call` path. Extra permission is a policy refusal today. A future -`approval_*` fallback comes before requiring MRTR. Interactive processes -stay `process_id` handles; Tasks must not replace them. +## Supported baseline -## 2026-07-28 +The repository explicitly tests MCP `2025-11-25` over both transports. It also tests negotiated `2026-07-28` through the pinned SDK without fallback. The newer negotiation does not add different execution behavior. These are repository compatibility targets; a client must negotiate a revision supported by both sides. -When a client negotiates 2026-07-28, the server may advertise enhancement -flags. Semantics of the live tools stay identical to 2025-11-25. This -revision is **progressive enhancement only**. +HTTP's transport floor is `2025-03-26`. Legacy `2024-11-05` HTTP+SSE is not a supported transport here. Tasks, subscriptions, multi-round-trip requests, and header-based tool routing are not required or implemented as the core execution path. The server dispatches JSON-RPC methods and tool names from the body. -Existing Auto tests that prefer 2026-07-28 and fall back to 2025-11-25 -prove fallback. They do **not** replace 2025-11-25-only coverage. +## What the client should inspect -## Out of scope +Read `initialize.instructions`, discover tools, then call `workspace_info` with a registered ID. Its `execution` object distinguishes permission from backend support, and reports file/process eligibility, PTY capabilities, serialization, sandbox, and network enforcement. Eligibility does not include transient occupancy. -- **2024-11-05 HTTP+SSE.** Not a target. `rmcp` 3.x does not provide that - transport. -- Implementing MRTR, Tasks, or subscriptions. Those remain optional - progressive enhancement and are **not** required for work/steer, - exec, patch, or a future approval flow. -- Browser Inbox UI (HTTP `/inbox` JSON is in this release). +For actual tool arguments, result interpretation, and retry rules, use [Agent Loop integration](agent-integration.md). Instruction queues use `work_id` and `intent_id`, not MCP Tasks. No protocol flag grants additional workspace permissions. ## Tests -`crates/server/tests/protocol_compat.rs` pins: - -1. Forced **2025-11-25** on stdio and HTTP (`ClientLifecycleMode::Initialize` - + `with_protocol_version(V_2025_11_25)`). -2. Forced **2026-07-28** on HTTP and stdio, with **no** legacy fallback - (`Auto { preferred_versions: [V_2026_07_28], legacy_version: None }`). - -After handshake, `tools/list` includes `workspace_info` and `tools/call` -matches the existing payload contract. Forced 2025-11-25 also covers -`read` / `apply_patch` / `exec_command` / `operation_status` and the -work/steer checkpoint (`work_open` → `/inbox` queue → `steer_claim_next` -→ `work_finish`). Forced 2026-07-28 uses the same `tools/call` surface. +`crates/server/tests/protocol_compat.rs` forces each tested revision on stdio and HTTP, then exercises tool discovery and calls. Tests that prefer a newer revision but allow fallback are separate evidence; they do not replace forced-baseline tests. -Client-facing execution semantics stay on that surface. `initialize.instructions` -states global invariants (request lifetime ≠ process lifetime, host is -not an OS sandbox, unenforced network is not permission). It does not -assert a network policy value; `workspace_info.execution.network` reports -the effective policy. `workspace_info` -with a `workspace_id` adds an `execution` object (permissions vs backend -support, `files.*.available` and `process.available` as permission and -backend support (not occupancy; `exec_command` or `apply_patch` may still -return `WORKSPACE_BUSY`), PTY, mutation lease, isolation, network). -`files.read.available` and `files.find.available` require read permission -and `file_read_supported`. `files.patch.available` requires write -permission and `file_write_supported`. `process.available` requires both -exec permission and backend support and does not include transient -occupancy; tool existence is `tools_exposed`. -`output_combined=true` means `read_process` exposes one combined stream; -stdout/stderr identity is not preserved. `exec_command` results add -`dispatch_status` (`confirmed` or `unknown`). Empty argv is -`INVALID_COMMAND` (rejected before process dispatch). Confirmed spawn -failure is `PROCESS_SPAWN_FAILED`: the backend confirmed that no managed -process was established. That is distinct from `dispatch_status=unknown`. -Runner UDS wire is `WIRE_PROTOCOL` 3. `ErrorBody` product codes travel on -that JSON, so a changed error vocabulary is a protocol bump. Handshake -mismatch is before-dispatch. Gateway and worker are the same CodeSpace -build; skew is detected, not tolerated. -Tool **names** do not -grow. `environment_id`, `cwd`, `tty_size`, and `process_resize` stay off -the client schema. +`http_contract`, `stdio_contract`, and `transport_contract` cover the transport adapters. Local test success does not establish the protocol revision used by a live ChatGPT account. See [ChatGPT connection status](chatgpt-connector.md). diff --git a/docs/runner-isolation.md b/docs/runner-isolation.md index bc3585a..2e2d91f 100644 --- a/docs/runner-isolation.md +++ b/docs/runner-isolation.md @@ -2,89 +2,45 @@ [English](runner-isolation.md) | [한국어](ko/runner-isolation.md) -**Target** execution isolation OS is a Linux container. **Current** -`exec_command` is a host process. When the Linux helper probe succeeds, -pipe and PTY spawn the same `codespace-linux-sandbox run --plan` argv -(bubblewrap + `no_new_privs`/seccomp; Codex translation stays inside -that process). Prepare / protocol / helper OS-spawn failure is -`PROCESS_SPAWN_FAILED`. After the managed helper process is spawned, -`run --plan` load, Restricted self-exec, Enabled proxy spawn, or inner -sandbox failure is a managed process exit. When the probe fails (macOS, no -bwrap), Restricted spawn is unsandboxed and `workspace_info` advertises `none`. -Enabled without a helper is `PROCESS_SPAWN_FAILED` (not host network). -Gateway unit tests may run on macOS. That is not a -claim that Linux isolation was verified on the development laptop. - -## What the compose fixture does - -[`deploy/compose.yml`](../deploy/compose.yml) is an **isolation fixture**. -It runs an unprivileged user (`uid 10001`), bind-mounts **only** the -workspace at `/workspace`, and sleeps. It does not ship -`codespace-mcp` / `codespace-patch`, and it is **not** connected to -`exec_command`. - -It does not mount: - -- host `$HOME` -- SSH agent socket -- `/var/run/docker.sock` -- gateway `.env`, Bearer files, or SQLite - -There is no runner control socket on the compose fixture. Do not add a -host Docker socket or a future control socket to this fixture by -accident. Opt-in `CODESPACE_RUNNER=uds` uses a **private** gateway↔worker -Unix socket off this fixture. The gateway creates a unique 0700 leaf -(`$TMPDIR/codespace-runner--/` or -`$CODESPACE_RUNNER_DIR/run--/`) and binds `$dir/runner.sock`. -Live sockets are probed with `connect`; only `ConnectionRefused` -leftovers are unlinked. `/tmp` itself is never chmodded. - -## macOS / no Docker - -`codespace-runner::PathSandbox` applies the same relative-path + symlink -+ special-file rules for unit tests and for `read` / `find` / versions. -That is workspace authorization, not race-proof I/O. File bytes, -metadata, mkdir, chmod, remove, and bounded walks go through isolated -`crates/file-system` (`codespace-fs`, no-follow `LOCAL_FS`). Live -processes may mutate the tree between PathSandbox's lstat and the -adapter open; no-follow I/O is the safety boundary. If Docker is not -used, **Linux container isolation is unverified**. Host -seccomp/AppArmor and Docker Desktop vs Linux engine differences are also -unverified. - -## Later process split - -Today `codespace-mcp` is one process by default. `crates/runner` hosts -`InProcessRunner` (`PathSandbox`, one `apply_patch` transaction, host -supervisor) and the opt-in Unix-socket `UdsRunner` client. The -worker is isolated `crates/codex-runtime` (`codespace-codex-runtime`): -`codex_process_hardening::pre_main_hardening()` stays the first line of -`main` (process hardening of the worker/helper, **not** a command -sandbox; no `ctor`). Then bind `$dir/runner.sock` (no parent chmod), -then **one** `InProcessRunner` for the process. Wire format is **u32 -length-prefix + CodeSpace JSON** (`protocol: 3`, Hello handshake, -`request_id` `rrpc-…`, events include `ProcessExited`), not App Server. -P0 UDS is 1:1: the gateway owns the worker child (`kill_on_drop`); -disconnect or gateway shutdown kills the worker and host children; -`process_id` does not survive; there is no reconnect. Runner `Replay` -is same-connection only. That **transport** is implemented; it -is opt-in (`CODESPACE_RUNNER=uds` / `CODESPACE_RUNTIME_BIN`) on the -**same host**. Linux command sandbox is a wrap of that same -`InProcessRunner` spawn (`helper probe` / `prepare` / `run --plan`), not a second transport rewrite. -Prefer `codex-uds` as the socket primitive; the Runner RPC stays a -CodeSpace contract. - -Linux isolation is still the target OS. Landlock, seccomp, PTY helpers, -UDS, filesystem mechanics, and network isolation are **not** a default -homegrown stack. Prefer upstream execution subgraphs -([codex-reuse.md](codex-reuse.md)), staged -process-hardening → PTY → UDS/path → filesystem → linux-sandbox → -network (filesystem is taken via `crates/file-system`; linux-sandbox -via the `crates/linux-sandbox` binary and `crates/linux-sandbox-protocol`). -`codex-linux-sandbox` can sit beside a -container; keep its `codex-core` **dev-dep** out of the product graph. -P0 network (`Enabled` + managed proxy) is taken. `codex-exec` stays -rejected. `codex-exec-server` is a reference / future backend, not a -forever reject. Gateway policy remains the only allow path. Do not -mount a host Docker socket or a future control socket on the compose -fixture by accident. +There are three separate questions: which process runs the work, whether commands are sandboxed, and whether execution moves into a container. Today the first two are implemented; container dispatch is not. + + + +## Host and worker execution + +`in-process` runs the supervisor inside `codespace-mcp`. `uds` (Unix domain socket) runs that supervisor inside `codespace-codex-runtime` on the same host. The worker starts with Codex process hardening and binds a private Unix socket. Hardening the worker does not sandbox its commands. + +The gateway creates a unique 0700 directory beneath its temporary directory or `CODESPACE_RUNNER_DIR`. The internal protocol is CodeSpace JSON with a u32 length prefix, handshake version 3, request IDs, and process-exit events. It is not Codex App Server RPC. Same-connection replay is not reconnect recovery. Gateway/worker disconnect ends the owned worker and its children; process handles are lost. + + + +## Linux command sandbox + +Both pipe and PTY execution use the same Linux helper path. On Linux, the runner probes `CODESPACE_LINUX_SANDBOX_BIN` or a sibling `codespace-linux-sandbox` binary. A successful probe enables preparation and sandboxed execution. The probe result is cached for that process. + +```text +Runner → helper prepare (CodeSpace JSON, protocol 1) + ← opaque plan pathname +Runner → managed helper run --plan + → Codex sandbox setup → requested command +``` + +Codex permission translation and sandbox arguments stay inside the binary-only helper. The plan is a private 0600 file consumed by the helper. The runner depends on the small protocol crate, not the sandbox implementation library. Restricted execution uses self-exec; enabled execution keeps a helper-owned proxy while waiting for its sandbox child. + +A failed initial probe permits unsandboxed host execution for `restricted`; the contract reports `command_sandbox: none` and no OS network enforcement. `enabled` requires the helper and fails without it. Once a probe succeeds, later prepare/protocol/spawn errors never fall back to unsandboxed execution. They report `PROCESS_SPAWN_FAILED`. Failure inside an already-started helper is observed as process termination; the public MCP result currently has no exit code. + +## Network and filesystem scope + +Restricted mode uses network namespace separation and restricted seccomp rules. Enabled mode uses an isolated network namespace with a managed HTTP proxy. Proxy bypass environment entries are cleared so loopback HTTP also follows the proxy. Direct host networking is not the fallback. Destination domain restrictions and compatibility with every network client are not implemented guarantees. + +Read the effective `workspace_info.execution` fields. File tools stay workspace-scoped independently of command sandboxing. `codespace-fs` provides no-follow I/O behind logical path checks. The patch helper has its own path checks and Codex patch options; command sandboxing does not automatically wrap all file tools. + +Sandboxed commands use a limited system PATH rather than mounting host toolchains from the user's home. Prepare the required compilers, package caches, and binaries in the execution environment. A command that works on the host may fail in the sandbox because its executable or dependency is unavailable. + + + +## Container fixture and verification + +`deploy/compose.yml` runs a non-root sleeper with only the selected workspace mounted at `/workspace`. It does not install the server, launch a Runner, or receive `exec_command` calls. Never describe starting this fixture as activating the MCP command sandbox. + +CI has a Linux isolation job that installs bubblewrap and requires helper availability for isolation tests. macOS checks cover host behavior, not Linux enforcement. Kernel escapes, Docker Desktop differences, and arbitrary remote deployments need separate assessment. See [operations](operations.md) for setup and [security model](security-model.md) for trust assumptions. diff --git a/docs/security-model.md b/docs/security-model.md index 4f0ad18..48a6640 100644 --- a/docs/security-model.md +++ b/docs/security-model.md @@ -2,138 +2,44 @@ [English](security-model.md) | [한국어](ko/security-model.md) -CodeSpace is not a kernel sandbox. CoS and cokacremote are not either. -Authorization is **gateway policy**. Linux container isolation is the -**target** execution OS; current `exec_command` is a host process with -workspace cwd. The Codex patch crate does not supply the product -boundary. Codex session config and `permissionProfile` are not an -allow path. Gateway and runner are both Rust; splitting languages would -not add a trust boundary. Primitive vs product: -[codex-reuse.md](codex-reuse.md). Execution-only substrate: -[execution-substrate.md](execution-substrate.md). +CodeSpace is designed for a personal, single-user deployment. The operator chooses which directories an external agent may use. File-tool permissions, command isolation, and network enforcement are distinct controls; inspect the effective workspace contract rather than inferring safety from a tool name. + + + ## Trust boundaries -1. **MCP client** — untrusted for authorization. It may send any tool - arguments, including `workspace_id`, `approved: true`, and absolute - paths. Those arguments never grant rights. -2. **Gateway (`crates/server` + `crates/policy`)** — trusted to - authenticate the transport (optional static Bearer on HTTP - experiments), select a registered workspace, and refuse work the - profile does not allow. -3. **Runner (`crates/runner`, in-process today)** — trusted to enforce - path policy (`PathSandbox`) and to supervise **host** processes for - an already-authorized action. Untrusted to see gateway secrets. A - later Unix-socket / container split keeps the same Rust workspace; it - is a process boundary, not a language one. Compose under `deploy/` is - an isolation fixture, not this process. -4. **Patch helper (`codespace-patch` + `crates/patch`)** — trusted to - parse/verify/apply Codex V4A **in-process inside the helper child**. - The gateway talks to that child over JSON stdin/stdout. Untrusted as a - sandbox (upstream standalone apply uses sandbox `None` and may follow - symlinks). This is not the retired `native/patch-worker`. - -## Authentication vs selection - -- Optional static Bearer is for **HTTP experiments only**. It is not an - OAuth server. Tokens must never appear in logs or error payloads. -- `workspace_id` is a selector. Knowing the id does not authenticate. -- `work_id` and `intent_id` are selectors. Knowing them does not authenticate. -- User-intent bodies are instructions. They never raise the workspace - profile or bypass path policy. -- ChatGPT conversation ids are not a trust base. - -## Workspace registry - -Workspaces are registered in **server configuration**, not by the model. - -Each entry maps `workspace_id` → `{ root, profile }`. - -Unknown ids are rejected. Paths are resolved **before** being passed to -the engine. Relative paths only. After resolve they must stay inside that -workspace root. - -## Profiles (MVP) - -| Profile | Meaning | -| --- | --- | -| `read-only` | Default. `read` / `find` / `workspace_info` / `operation_status`. No patch, no shell. | -| `workspace-write` | Explicit. Mutating patch and shell **inside** the workspace. A live shell can delete workspace files; the product says so honestly. | -| `host-admin` | **Excluded from MVP.** | - -A busy shell holds the workspace write lock. Other mutating work waits or -fails with `WORKSPACE_BUSY`. - -## Product path policy (always, even if the crate would allow it) - -- Relative paths only. -- Reject symlink targets and special files (devices, sockets, fifos). -- Reject Add File when the destination already exists. -- Reject Move when the destination already exists. -- Do not follow `..` out of the workspace. -- Do not pass host-absolute paths from the model into the engine. -- `PathSandbox` authorizes the logical path. It does **not** make the - later open safe. Live `read` / `find` may race a process that swaps a - directory for a symlink (TOCTOU). -- Runner-owned file operations and rollback use `codespace-fs`, a thin - adapter over Codex `LOCAL_FS` with `follow_symlinks: false`. -- `apply_patch` mutation uses `crates/patch` → - `apply_patch_with_options` + `LOCAL_FS` `follow_symlinks: false` - (`sandbox: None`). Helper preflight and post-hash may still use - `std::fs`; that is not the no-follow boundary. -- The shared primitive is Codex `LOCAL_FS` no-follow, not `codespace-fs` - itself. `sandbox: None` does not disable that pin. - -See [behavior-differences.md](behavior-differences.md). - -## Runner isolation (Linux) - -**Current:** `exec_command` runs on the host as argv + workspace cwd + -`env_clear`. Path sandboxing applies to `read` / `find` / versions / -rollback, not as a Linux namespace. - -**Target / fixture:** unprivileged container user. Mount the workspace -at `/workspace` (or an equivalent dedicated volume). Do **not** mount: - -- host home -- SSH agent socket -- `/var/run/docker.sock` -- gateway `.env`, Bearer files, SQLite - -[`deploy/compose.yml`](../deploy/compose.yml) demonstrates those -properties with `sleep infinity`. It is not connected to -`exec_command`. There is no runner control socket today. - -Gateway unit tests may run on macOS. That is not a claim that Linux -isolation was verified on the development laptop. - -## Patch honesty - -Statuses: `applied`, `checked`, `rejected`, `failed_rolled_back`, -`failed_partial`, `unknown`. - -- Successful `check_only` preview → no file changes (`checked`). -- Preflight / policy failure → no file changes (`rejected`). -- Apply failure that restored snapshots → `failed_rolled_back`. -- Apply failure with leftover drift → `failed_partial` or `unknown`. -- Never report `applied` unless disk hashes match the helper's claimed - `after_version` (deletes must be absent). -- Never `git reset --hard`. -- Never treat HTTP timeout as rollback or as success. - -After `Store::begin` mints an `operation_id`, execution errors include -that id on `ErrorBody`. Transport and pre-`begin` refusals do not. - -## Process honesty - -`process_id` values are minted by the server. Clients cannot invent -handles. Output is read by cursor and bounded (256 KiB per process). -Time, live process count, and **completed-handle retention** (15 minutes -or 64 completed slots) are limited. Disconnect does not imply the -process died. Process state is volatile: it is not stored in SQLite. +The gateway owns authentication, workspace registration, and permission decisions. `workspace_id`, `work_id`, instruction text, and client claims such as `approved: true` do not grant rights. HTTP optionally checks a static Bearer token; this is not OAuth or per-user authorization. + +The Runner executes approved requests. `PathSandbox` checks logical file scope; `codespace-fs` performs no-follow I/O for Runner file operations. A pre-check alone cannot prevent a concurrent process from replacing a path. The patch helper separately applies CodeSpace path policy and uses the Codex no-follow patch options; helper preflight and post-hash code still contain direct filesystem calls. Do not describe all file handling as one race-proof primitive. + +The optional UDS worker separates processes on the same host. Its socket lives in a private directory, but process separation is not an OS command sandbox. The Linux helper supplies the command sandbox when its probe succeeds. [Runner isolation](runner-isolation.md) defines those conditions and failure behavior. + + + + + +## Permissions and isolation + +`read-only` permits reads but denies patches and exec. `workspace-write` permits both; an arbitrary command can change or delete workspace files. Relative-path restrictions on MCP file tools do not by themselves confine a host command. + +Without a Linux helper, a restricted-policy workspace can execute unsandboxed host commands, and `network.enforcement` is `none`. This is not a guarantee of network denial. An enabled-network workspace requires the helper and does not fall back to host networking. The enabled proxy currently permits all destination domains; it is not a domain allowlist feature. + +The operator-registered root is a trust anchor. Keep tokens, the operations database, gateway configuration, and other secrets outside managed roots. Do not expose a privileged host, Docker socket, or SSH agent through a workspace merely because file tools validate relative paths. + + + + +## Operation and recovery safety + +Version checks prevent applying a patch to an unexpected file version. Operation keys detect duplicate patch requests; they are not authorization tokens. Workspace occupancy serializes mutating patch and exec work. There is no queue scheduler or durable process recovery. + +Patch snapshot restoration is best effort and is not an all-failure rollback guarantee. On `unknown`, partial failure, or post-apply verification error, inspect affected files. See [patch behavior](behavior-differences.md) and [integration recovery rules](agent-integration.md). + + + +## Evidence and limits + +Security test sources cover path escapes, special files, authentication responses, operation replay, and concurrent work. Linux helper tests cover sandbox and proxy behavior. Test coverage is not a kernel-escape audit or proof for every deployment. See the [coverage inventory](../tests/adversarial-report.md). -## Logging - -Redact Authorization headers, Bearer tokens, and `.env` values. Prefer -structured fields (`workspace_id`, `operation_id`) over dumping raw -requests. There is no separate audit subsystem. +Structured stderr logging includes redaction helpers. Avoid logging raw tool requests or secrets; there is no separate tamper-resistant audit service. Public HTTPS/ChatGPT deployment and multi-tenant authorization are not established by local MCP tests. diff --git a/docs/translations.json b/docs/translations.json index c7de338..af8f671 100644 --- a/docs/translations.json +++ b/docs/translations.json @@ -37,13 +37,18 @@ "source": "docs/index.md", "translation": "docs/ko/index.md", "anchors": [ + "choose-a-guide", "codespace-documentation", + "codespace-문서", "codespace-문서", + "codespace-문서-안내", "find-your-next-step", - "다음-단계" + "다음-단계", + "다음-단계", + "목적에-맞는-문서-찾기" ], - "source_sha256": "aab2117e9bc7b9181de82b470378177d99c0d893da21666852d3656bde23ee7d", - "translation_sha256": "fa275f3ee817600988401c80761fb8749e77abd84a7165f54fe22da4c477dc09" + "source_sha256": "2fb1f05b450ed1a948310d4fdafe1781ef14979990f2ecfebfae98f7ebdab7ca", + "translation_sha256": "d4ea3230b9f759e5bca3f59ca45a115341d901eb7d66758af935fe113ccb86b3" }, { "id": "getting-started", @@ -53,68 +58,137 @@ "source": "README.md", "translation": "README.ko.md", "anchors": [ + "available-tools", "codespace", + "execution-and-current-limits", "license", "run", + "start-with-a-registered-workspace", "status", + "상태", + "실행", "라이선스", "상태", - "실행" + "실행", + "실행-방식과-현재-제약", + "작업-공간을-등록하고-시작하기", + "제공하는-도구" ], - "source_sha256": "1aa10bfe05a0d694c09cf472c150040d4cad046cd72c5f03613965310c3dbebd", - "translation_sha256": "dbafbaf7197cfa0844ba6fed773366660fb3c218bcb2f255365056a94ec147a6" + "source_sha256": "fbeb688c943ac78e3ae3a0441dedce9c553dd25470eadabcc037429990263c32", + "translation_sha256": "32c41ce394799ac7603f463e9ef7d5a8afab3dc61845c45eef05d03eab11e319" }, { - "id": "operations", + "id": "agent-integration", "section": "start", "order": 2, + "route": "/guide/agent-integration", + "source": "docs/agent-integration.md", + "translation": "docs/ko/agent-integration.md", + "anchors": [ + "agent-loop-연동", + "connect-an-agent-loop", + "connect-and-inspect-capabilities", + "handle-user-instructions-and-finish", + "read-and-patch-a-file", + "retry-and-recover-deliberately", + "run-and-observe-a-command", + "명령-실행과-결과-확인", + "사용자-지시-처리와-완료", + "연결과-기능-확인", + "재시도와-복구", + "파일-읽기와-패치" + ], + "source_sha256": "4ce4d006b182119e3489f8055d70edccc08b8095bff0711b75cdcee7ace175cd", + "translation_sha256": "9e71ea5818103fc7a5bc3d5f2fa93ed21ec84068c41b5cc1b091725cada47990" + }, + { + "id": "operations", + "section": "start", + "order": 3, "route": "/guide/operations", "source": "docs/operations.md", "translation": "docs/ko/operations.md", "anchors": [ "install", + "installation-and-operations", "linux-isolation-fixture", + "linux-격리-픽스처", "linux-격리-픽스처", "logs", + "logs-limits-and-recovery", + "mvp-흐름-재현", "mvp-흐름-재현", "operations", + "optional-unix-socket-worker", "recovery-after-disconnect-or-restart", + "register-a-workspace", "reproduce-the-mvp-flow", "run-the-gateway", + "start-the-server", + "troubleshooting", + "verify-the-first-connection", "what-this-document-does-not-verify", "workspace-registry", + "게이트웨이-실행", + "로그", + "연결-끊김-또는-재시작-후-복구", + "운영", + "워크스페이스-레지스트리", + "이-문서가-검증하지-않는-것", "게이트웨이-실행", "로그", + "로그와-제한-복구", + "문제-해결", + "서버-시작", + "선택적-unix-소켓-worker", "설치", + "설치와-운영", "연결-끊김-또는-재시작-후-복구", "운영", "워크스페이스-레지스트리", - "이-문서가-검증하지-않는-것" + "이-문서가-검증하지-않는-것", + "작업-공간-등록", + "첫-연결-확인" ], - "source_sha256": "c296a1052686a33c4ef7e5e22696c62d4ba58adf622833b61db4375e811faa82", - "translation_sha256": "bbc4baab8e867b940f837982056c7ddfff667c8901289665b807c4dc31b0a234" + "source_sha256": "984ff0379f77362949c7ce1202d2f91485dd5fc623941199c179f021db78637f", + "translation_sha256": "7a85ef6419c22c789ce6135cee83170418aad9d8bbda7fc89da4813c04ac69ef" }, { "id": "chatgpt-connector", "section": "start", - "order": 3, + "order": 4, "route": "/guide/chatgpt-connector", "source": "docs/chatgpt-connector.md", "translation": "docs/ko/chatgpt-connector.md", "anchors": [ "chatgpt-account-connection", + "chatgpt-connection-status", "chatgpt-connector-experiment", + "chatgpt-specific-requirements", + "chatgpt-계정-연결", + "chatgpt-커넥터-실험", "chatgpt-계정-연결", + "chatgpt-연결-상태", "chatgpt-커넥터-실험", + "chatgpt에서-별도로-확인할-조건", + "credentials", + "local-mcp-clients", "local-stdio-verified-by-cargo-test", "local-streamable-http-experiment", "secrets", + "verification-status", + "로컬-stdio-cargo-test로-검증", + "로컬-streamable-http-실험", + "비밀", + "검증-상태", + "로컬-mcp-클라이언트", "로컬-stdio-cargo-test로-검증", "로컬-streamable-http-실험", - "비밀" + "비밀", + "인증-정보" ], - "source_sha256": "ecda1e91f3a47fa9c16a09a9e846afa66aaa624c0b756db6ddc88f9f9a325aec", - "translation_sha256": "01ff41a3e3e02a06d08b9a970ae7d0bd73b3254a1a4339d571f73ae30a6049c5" + "source_sha256": "ca419b80e5208198c10c55193aacf797f18f8e105acb62293b9081991b113c71", + "translation_sha256": "d225b7b21dc0a7f65f971452c7bad1b4b43dc5e581f20ea0e593ecb1da0952ca" }, { "id": "architecture", @@ -126,27 +200,46 @@ "anchors": [ "architecture", "current-layout", + "extension-boundaries", "id", "identity", "ids", "mvp-tools", + "mvp-도구", "mvp-도구", "out-of-scope-initial", "patch-apply-pipeline", + "patch-transaction", + "process-lifetime", "protocol-compatibility", "repository-layout", + "responsibilities-and-state", "target-layout", + "tool-and-protocol-references", + "목표-배치", + "저장소-배치", + "정체성", + "초기-범위-밖", + "패치-적용-파이프라인", + "프로토콜-호환성", + "현재-배치", + "구성-요소의-책임과-상태", + "도구와-프로토콜-참고", "목표-배치", "아키텍처", "저장소-배치", "정체성", "초기-범위-밖", "패치-적용-파이프라인", + "패치-처리-과정", + "프로세스-수명", "프로토콜-호환성", - "현재-배치" + "현재-배치", + "현재-실행-구조", + "확장-시-유지할-경계" ], - "source_sha256": "1e2cc610d5339aeaee3e15c8885ffa0e5ed4f6fce5cefc6080368e99f7940cdc", - "translation_sha256": "7f4705198510476da9c44c51ca5d4595fb532a3d2a9799deef5dda14781ad196" + "source_sha256": "3e7a03a870203509f889b55470e67ee7521e19520e301fc2f3488d10b90bdf88", + "translation_sha256": "9ee671b1075bacf41e815b24486e6a81b6ba8514163d4a3a46713cb99ff45b0e" }, { "id": "execution-substrate", @@ -157,30 +250,55 @@ "translation": "docs/ko/execution-substrate.md", "anchors": [ "approval-and-mcp-revision", + "command-exec-shape-vs-crates", + "command-exec-형태-대-크레이트", "commandexec-shape-vs-crates", "commandexec-형태-대-크레이트", + "environment-and-identity", + "execution-and-observation", + "execution-contracts", "execution-substrate", "four-axes-target-domain", + "fs-watch-and-search", + "fs-watch와-검색", "fswatch-and-search", "fswatch와-검색", "hooks-and-skills", "invariant", + "maintaining-the-boundary", "pathsandbox-vs-codespace-fs", + "pathsandbox와-codespace-fs", "pathsandbox와-codespace-fs", + "policy-and-mechanism", "roadmap-implementation-later", "scheduler-after-the-single-write-lock", "take-leave-concepts", + "what-remains-unimplemented", + "가져오기-빼기-개념", + "네-축-목표-도메인", + "로드맵-구현은-나중", + "불변식", + "스케줄러-단일-쓰기-잠금-이후", + "승인과-mcp-리비전", + "실행-기반", + "훅과-스킬", "가져오기-빼기-개념", + "구현-경계-유지", "네-축-목표-도메인", "로드맵-구현은-나중", "불변식", "스케줄러-단일-쓰기-잠금-이후", "승인과-mcp-리비전", + "실행-계약", "실행-기반", + "실행-환경과-식별자", + "실행과-결과-관측", + "아직-제공하지-않는-기능", + "정책과-실행-구현", "훅과-스킬" ], - "source_sha256": "42159d3e4611f04f9cf4743892f7eb089226d9350163152c9386bf82f49908cb", - "translation_sha256": "f8e9397b4b24125ed69061288f0b9de843986a807f1e1e229c767bde5fb08748" + "source_sha256": "43d646da27f7b60ea8ecf678a4fbcf181f8dbf0dd57f54c73715a0fe9fd29212", + "translation_sha256": "a3cf17570723de0cddda093e0cfecc5e4f86e3fef5593ecdae202021fbbc858d" }, { "id": "protocol-compatibility", @@ -191,19 +309,32 @@ "translation": "docs/ko/protocol-compatibility.md", "anchors": [ "2026-07-28", + "_2026-07-28", "core-baseline", + "mcp-protocol-compatibility", + "mcp-프로토콜-호환성", "out-of-scope", "protocol-compatibility", + "supported-baseline", "tests", "what-core-must-not-require", + "what-the-client-should-inspect", + "범위-밖", + "시험", + "프로토콜-호환성", + "핵심-기준", + "핵심이-요구하면-안-되는-것", "범위-밖", "시험", + "지원-기준", + "클라이언트가-확인할-정보", + "테스트", "프로토콜-호환성", "핵심-기준", "핵심이-요구하면-안-되는-것" ], - "source_sha256": "c4d909004edcc56813e4576bf6e3d2f876786fcf1a3ef84dc72ae81259f1e199", - "translation_sha256": "81e285cfbc852bbdd5fb58cbbbebd27eaf8aaa326e69d668378b2d2372146079" + "source_sha256": "f03a8c2f2ec3c4a9a28c78efd4b5c6f2acb62b0ed9271ccb8810f6808fa531c0", + "translation_sha256": "cf8103da72e6e189efb2181a95ba9df01292b606df52f4e7096ae4a68254a239" }, { "id": "behavior-differences", @@ -214,16 +345,25 @@ "translation": "docs/ko/behavior-differences.md", "anchors": [ "behavior-differences", + "patch-behavior-and-recovery", "patch-request-contract", + "results-and-recovery-limits", "transport", "write-lock", + "write-lock-and-transport", + "동작-차이", + "쓰기-잠금", + "전송", + "결과와-복구-한계", "동작-차이", "쓰기-잠금", + "쓰기-점유와-전송", "전송", + "패치-동작과-복구", "패치-요청-계약" ], - "source_sha256": "23f9a96282eee32750388bcb05cdae7ce9ad2b461d637cc67fcf4a6f1c39aa30", - "translation_sha256": "2862a4c4839b8fce67edd2ed40b1ba9e3b0f996e4f26b4dd3bb454d865fc1941" + "source_sha256": "ea05b25bb3134778841806bcaf196443449adb1bfdf572b7968509ca671c89a4", + "translation_sha256": "e46f9465a7183244d758402f16849a54bbf335718793c45702b1686c6eb5829d" }, { "id": "security-model", @@ -234,8 +374,11 @@ "translation": "docs/ko/security-model.md", "anchors": [ "authentication-vs-selection", + "evidence-and-limits", "logging", + "operation-and-recovery-safety", "patch-honesty", + "permissions-and-isolation", "process-honesty", "product-path-policy-always-even-if-the-crate-would-allow-it", "profiles-mvp", @@ -243,19 +386,30 @@ "security-model", "trust-boundaries", "workspace-registry", + "러너-격리-linux", + "로깅", + "워크스페이스-레지스트리", + "인증-대-선택", + "제품-경로-정책-크레이트가-허용해도-항상", + "패치-정직성", + "프로세스-정직성", + "프로필-mvp", + "검증-근거와-한계", + "권한과-격리", "러너-격리-linux", "로깅", "보안-모델", "신뢰-경계", "워크스페이스-레지스트리", "인증-대-선택", + "작업과-복구의-안전성", "제품-경로-정책-크레이트가-허용해도-항상", "패치-정직성", "프로세스-정직성", "프로필-mvp" ], - "source_sha256": "62ec79114259d72b9e74fcdcbcc6fa6ed044271f052106dbbb9aeed645bfe2d3", - "translation_sha256": "9f34f3b5f25c4bf0f8980574df1f9ae7ab558b8d917a4d6e1ac84ce60eae436c" + "source_sha256": "3ba23fbe3b884062601e9484c87a24dd52a3e2aa618598b8b83c01865a1a007b", + "translation_sha256": "c64b086cf638757a1462ae02f84173f2628e0bcf1c55cd78a3ce607c76fdb234" }, { "id": "runner-isolation", @@ -265,17 +419,28 @@ "source": "docs/runner-isolation.md", "translation": "docs/ko/runner-isolation.md", "anchors": [ + "compose-픽스처가-하는-일", "compose-픽스처가-하는-일", + "container-fixture-and-verification", + "host-and-worker-execution", "later-process-split", + "linux-command-sandbox", + "linux-명령-샌드박스", + "macos-docker-없음", "macos-docker-없음", "macos-no-docker", + "network-and-filesystem-scope", "runner-isolation", "what-the-compose-fixture-does", + "이후-프로세스-분리", + "네트워크와-파일-접근-범위", "러너-격리", - "이후-프로세스-분리" + "이후-프로세스-분리", + "컨테이너-실험-구성과-검증", + "호스트와-worker-실행" ], - "source_sha256": "feaebeb6072559c48dc356f3810c0563303d4e5b89013d3ac2930613062a8ac4", - "translation_sha256": "b4f54f498e03852ea2a46c812988397bb72679fe68ab91220cdf72c03e0c2cfd" + "source_sha256": "c9343ac59489e008a55f90d0fbfa7a66890b03abc08c4a65729685db602c72c1", + "translation_sha256": "c7c2298fedf0ebec6d527663d03575460cc3ae83f7f1277d30ddd6a25942c530" }, { "id": "error-codes", @@ -285,15 +450,26 @@ "source": "docs/error-codes.md", "translation": "docs/ko/error-codes.md", "anchors": [ + "dispatch-and-completion", "error-codes-and-transport-vs-execution", + "errors-and-uncertain-results", "execution-error-codes-tool-results", + "tool-error-codes", + "transport-failures", "transport-failures-no-operation", + "실행-오류-코드-도구-결과", + "오류-코드와-전송-대-실행", + "전송-실패-작업-없음", + "도구-오류-코드", "실행-오류-코드-도구-결과", + "실행-요청과-완료의-구분", "오류-코드와-전송-대-실행", + "오류와-불확실한-결과", + "전송-실패", "전송-실패-작업-없음" ], - "source_sha256": "053b227c025872519616b8273715dc50b429407419a2b001974ea82099bec6e4", - "translation_sha256": "31027ef820cd05441d3fe534fd5367b6b0ef4b0f9d14b17799b5abcf4e98aceb" + "source_sha256": "1bd9af29f2af3a0a56e4962264079941b50144cf29914477e82f18f578cc4df1", + "translation_sha256": "0a89b1267b5040f3c614acef681072112a9881939e18a40febebde1e6203bfb2" }, { "id": "codex-reuse", @@ -303,16 +479,24 @@ "source": "docs/codex-reuse.md", "translation": "docs/ko/codex-reuse.md", "anchors": [ + "apply-patch-패턴-격리이지-크레이트-너비가-아님", "apply_patch-패턴-격리이지-크레이트-너비가-아님", + "authority-stays-in-codespace", + "boundaries", "candidates-at-pin-6b9826e", "code-allowed-authority-forbidden", + "codespace에-남는-것", "codespace에-남는-것", + "codespace의-codex-재사용-범위", "codex-reuse-product-vs-primitive", + "codex-재사용-제품과-프리미티브", "codex-재사용-제품과-프리미티브", "conditional-active-evaluation", + "connected-components", "core-vs-adapter", "experimental-backend-not-now", "future-environment-not-p0", + "how-codespace-uses-codex", "internal-protocol-candidate", "isolated-layer-transitive-codex-protocol", "next-implementation-wp", @@ -321,17 +505,39 @@ "reject", "reuse-now-in-code", "staged-take-when-those-wps-exist", + "the-apply-patch-pattern-isolation-not-crate-width", "the-apply_patch-pattern-isolation-not-crate-width", "unit-of-reuse-is-a-subgraph", + "updates-and-future-work", "what-stays-codespace", "why-supervisor-code-still-exists", + "감독-코드가-아직-있는-이유", + "거절", + "격리-층-전이-codex-protocol", + "내부-프로토콜-후보", + "다음-구현-wp", + "단계적-가져오기-그-wp가-생길-때", + "실험적-백엔드-지금은-아님", + "이후-environment-p0-아님", + "재사용-단위는-서브그래프", + "재사용-선호-그-wp가-올-때", + "정책-대-메커니즘", + "조건부-적극-평가", + "지금-재사용-코드에서", + "코드는-허용-권한은-금지", + "핀-6b9826e의-후보", + "핵심-대-어댑터", "감독-코드가-아직-있는-이유", "거절", "격리-층-전이-codex-protocol", + "권한-결정은-codespace에서-수행", "내부-프로토콜-후보", "다음-구현-wp", "단계적-가져오기-그-wp가-생길-때", + "실제-연결된-구성-요소", "실험적-백엔드-지금은-아님", + "어댑터-경계", + "업데이트와-향후-검토", "이후-environment-p0-아님", "재사용-단위는-서브그래프", "재사용-선호-그-wp가-올-때", @@ -342,8 +548,8 @@ "핀-6b9826e의-후보", "핵심-대-어댑터" ], - "source_sha256": "d22a450bcaa323390adc8a49ccbe99f8876e3fc73dab9e134838d45fa05bc7e6", - "translation_sha256": "4cca24552254c99cb127614837fb9963083c9a289363317601929aad4a9648de" + "source_sha256": "408fed15492f21f73f9c1cf1c97ed43b4e964905db68508cad9fb3cd5f9be6dd", + "translation_sha256": "7c206a8c5f58b57460d3a35d4ae74767ca1e22b0621b27f8db17d0657036347d" }, { "id": "upstream-lock", @@ -353,19 +559,32 @@ "source": "docs/upstream-lock.md", "translation": "docs/ko/upstream-lock.md", "anchors": [ + "cargo-workspace와-잠금-파일", + "check-and-update", + "codex-고정-버전", + "deployment-pin", "deployment-pin-w06", + "pinned-codex-revision", "promotion-rule", "upstream-lock", "what-is-reused-vs-rejected", "why-file-copy-vendor-is-forbidden", + "workspace-and-lockfiles", + "배포-핀-w06", + "승격-규칙", + "업스트림-고정", + "재사용하는-것과-거절하는-것", + "파일-복사-벤더가-금지인-이유", + "배포-기준-버전", "배포-핀-w06", "승격-규칙", "업스트림-고정", "재사용하는-것과-거절하는-것", - "파일-복사-벤더가-금지인-이유" + "파일-복사-벤더가-금지인-이유", + "확인과-갱신" ], - "source_sha256": "97bc1e8932861350d8556f2e4830a67d8529127773634914eec46743368aeafe", - "translation_sha256": "fda2318e052d44f3276e6a7b3bb208e485c1963669ac800f1b9551607c6c9085" + "source_sha256": "00ae232546cc64b203cd4329a8f37490794045cda51911f610b1c049a0c0b80e", + "translation_sha256": "e7dc572e16802284728dfb6cf3b6bac7f500d3e3b3f7cbc93c20eb917330d5c3" }, { "id": "upstream-update", @@ -376,16 +595,28 @@ "translation": "docs/ko/upstream-update.md", "anchors": [ "checklist", + "codex-의존성-업데이트", "forbidden", "local-gate", + "release-and-rollback", + "review-the-candidate", + "updating-codex-dependencies", "upstream-pin-update", + "validate-before-submission", + "금지", + "로컬-게이트", + "업스트림-핀-갱신", + "점검-목록", "금지", "로컬-게이트", + "반영과-되돌리기", "업스트림-핀-갱신", - "점검-목록" + "점검-목록", + "제출-전-검증", + "후보-검토" ], - "source_sha256": "4eb34720223aff91a19cc8c9c4ab7b67a8cf9261795ee4e151cdb838250f68be", - "translation_sha256": "c0dbe1f6ab4f3d7a8ec3ba5605c061a70b605adca505fe0f54a5b7fee7d7ab58" + "source_sha256": "efe8b1d11db41148ec6dfd08a67b52fd72d2e32a480123f27668feff9e39f8da", + "translation_sha256": "fe4f0732755820b3b2db72190770359663ca6c6f2fcbc54227f714152ef7682d" }, { "id": "documentation", @@ -395,15 +626,28 @@ "source": "docs/documentation.md", "translation": "docs/ko/documentation.md", "anchors": [ + "build-and-preview", "documentation-site", + "editorial-review", + "maintaining-the-documentation", "public-documentation-and-the-documentation-site", "publication", + "publication-and-attribution", + "registry-and-compatibility", + "게시", + "공개-문서와-문서-사이트", + "문서-사이트", "게시", + "게시와-출처", "공개-문서와-문서-사이트", - "문서-사이트" + "내용과-표현-검토", + "문서-등록과-호환성", + "문서-사이트", + "문서-유지보수", + "빌드와-미리-보기" ], - "source_sha256": "7b1b0c3c3a3ce0931b81c5acba3b097c2e4695852743ee431cea0e8711a2d24a", - "translation_sha256": "bca6da943b0b5b73b1850da7f88ec7a3e91fe011e2546930156cc80996ece4da" + "source_sha256": "94b99eb403e8e8c9410833759dad94641d05c41049e5c4780825172c267740e8", + "translation_sha256": "36979e5054651813622c151193dd402e98ed22320fd6ac7d829fbe7661bfead6" } ] } diff --git a/docs/upstream-lock.md b/docs/upstream-lock.md index 2e30ce1..75771e3 100644 --- a/docs/upstream-lock.md +++ b/docs/upstream-lock.md @@ -1,110 +1,42 @@ -# Upstream lock + + +# Pinned Codex revision [English](upstream-lock.md) | [한국어](ko/upstream-lock.md) -CodeSpace reuses OpenAI Codex as a **pinned** git submodule. It does -not vendor a single source file, wrap the standalone `apply_patch` -binary as the security boundary, or follow `main`. +All current Codex execution adapters use the Git submodule in `third_party/codex`. A fixed commit makes their shared implementation reproducible. It does not automatically update when upstream publishes a release. -**Current code reuse** is `codex-apply-patch` (parse / verify / apply of -V4A) via `crates/patch`, plus `codex-process-hardening` (`codespace-patch` -and `codespace-codex-runtime`) and `codex-uds` (runtime worker bind). -That pin is the execution-implementation supply; it is not a vow that -only equally narrow crates may follow. Product runtime (App Server, -`codex-core`, `codex-exec`, login, models) stays out of **core**. A -cohesive **execution subgraph** may be taken in an isolated adapter -([codex-reuse.md](codex-reuse.md)). Codex types must not leak into -`crates/domain` or the MCP surface. Execution-only rules: -[execution-substrate.md](execution-substrate.md). + -## Deployment pin (W06) +## Deployment pin | Field | Value | | --- | --- | -| Project | [openai/codex](https://github.com/openai/codex) | -| License | Apache-2.0 (see root `NOTICE`) | +| Project | [OpenAI Codex](https://github.com/openai/codex) | +| License | Apache-2.0; attribution in [NOTICE](../NOTICE) | | Tag | `rust-v0.154.0` | | Commit | `6b9826e3aa83b1a5947db50f4332cb9c65f1b340` | -| Path | `third_party/codex` git submodule | -| Crate | `codex-apply-patch` via Cargo path dependency from `crates/patch` | -| Apply options | `PreserveLineEndings`, `follow_symlinks: false` | -| Parity | subset in `tests/parity/` and `crates/patch` tests; not the full upstream suite | +| Path | `third_party/codex` | +| Consumers | Patch, runtime worker, PTY, filesystem, Linux sandbox and proxy adapters | +| Patch options | `PreserveLineEndings`, `follow_symlinks: false` | -`crates/patch` is an **isolated Cargo workspace** (excluded from the repo -root workspace) so Codex crates keep their own `workspace.dependencies`. -Its `Cargo.lock` starts from the pinned Codex lockfile so transitive -crates (for example matching `rama-*` alphas) do not float. Codex -`[patch.crates-io]` git forks are copied into `crates/patch/Cargo.toml`. +See [connected components](codex-reuse.md) for the exact adapter responsibilities. Patch tests cover selected parity cases, not the entire upstream suite or all Codex behavior. -The adapter calls `parse_patch`, then product path policy (including -symlink-ancestor rejection), then `apply_patch_with_options` in the same -process with `LOCAL_FS`. It does not invoke `git apply` or the standalone -`apply_patch` binary. Passing `sandbox: None` to the library is **not** -the product sandbox; policy + no-follow I/O + the Linux runner are. + + -On macOS, `/var` is a symlink to `/private/var`. The adapter canonicalizes -the workspace root before building the `PathUri` cwd so no-follow walks -do not fail on that host alias. +## Workspace and lockfiles -## Why file-copy vendor is forbidden +Adapters have isolated Cargo workspaces so upstream workspace dependencies remain usable without copying and maintaining a forked patch parser. Preserve adapter lockfiles and required upstream Cargo patches. The filesystem and sandbox adapters pin matching Rama alpha dependencies to avoid resolving an incompatible mixture of alpha and stable releases. -`codex-apply-patch` 0.154.0 is a workspace crate. It depends on other -crates in the same repo, including: +The patch adapter calls the library inside `codespace-patch`; it does not invoke upstream's standalone `apply_patch` executable. `LOCAL_FS` and path utilities are implementation dependencies. Codex user settings do not grant workspace access. -- `codex-exec-server` -- `codex-utils-absolute-path` -- `codex-utils-path-uri` -- tree-sitter related workspace crates + -Copying `apply-patch` sources into `crates/patch` would either fail to -build or quietly fork the engine. CodeSpace therefore uses: +## Check and update -```text -git submodule add https://github.com/openai/codex.git third_party/codex -git -C third_party/codex checkout 6b9826e3aa83b1a5947db50f4332cb9c65f1b340 +```bash +PIN_ONLY=1 ./scripts/check-upstream-pin.sh ``` -and a path dependency, not a crates.io moving version. - -## What is reused vs rejected - -**On this pin, in code:** parse, hunk verification, apply APIs, and -selected upstream fixtures for parity (`crates/patch` → -`codex-apply-patch`); `pre_main_hardening()` (`codex-process-hardening`); -private socket directory + bind (`codex-uds`); interactive spawn -(`crates/pty` → `codex-utils-pty`); no-follow filesystem I/O -(`crates/file-system` → `LOCAL_FS`). - -**Rejected as product defaults** even if that crate allows them: -symlink follow, sandbox `None` standalone CLI, host-absolute paths from -the model, silent `git apply`. - -**Rejected as CodeSpace *core* dependencies:** any Codex crate path -dep, including `codex-protocol` types. Product runtime stays out -everywhere: App Server, `codex-core`, `codex-exec`, login, models. -See [codex-reuse.md](codex-reuse.md). Prefer-reuse at this SHA, **not -yet wired:** `codex-file-search`. Active evaluation: -`codex-shell-command`, `codex-linux-sandbox` -(transitives `codex-sandboxing`, `codex-network-proxy`; -`codex-protocol` allowed only in the adapter). `codex-exec-server-protocol` -is an internal DTO candidate. `codex-exec-server` is a reference / -future backend, not a forever reject. `codex-git-utils` / `codex-worktree` -stay unwired. - -Do not wrap `codex-rs` standalone `apply_patch` and call that a sandbox. -Preview / `check_only` is implemented through library parse plus -CodeSpace preflight, not by assuming `apply_patch --check` exists. - -Do not treat Codex session `permissionProfile` as the allow path. -Gateway policy is the only authorization authority. - -## Promotion rule - -1. Record the candidate (W01). -2. W06: submodule + adapter + parity subset (this pin). -3. If parity fails, **do not ship**. Change adapter options or pick - another revision; do not paper over mismatches. -4. W13: follow [upstream-update.md](upstream-update.md). Never `git - submodule update --remote` to latest Codex `main` as a deploy step. - `scripts/check-upstream-pin.sh` must stay red if the SHA or patch - tests fail. +This checks only the submodule revision against this document. Without `PIN_ONLY`, the script also runs patch tests; it does not replace the other adapter gates. Use the [complete update procedure](upstream-update.md) before changing the pin. Do not use `git submodule update --remote` as a deployment step. diff --git a/docs/upstream-update.md b/docs/upstream-update.md index c94943e..5221eba 100644 --- a/docs/upstream-update.md +++ b/docs/upstream-update.md @@ -1,68 +1,34 @@ -# Upstream pin update + + +# Updating Codex dependencies [English](upstream-update.md) | [한국어](ko/upstream-update.md) -Changing the Codex revision is a **deliberate release**, not `git -submodule update --remote` to `main`. If the parity subset fails, **do -not ship**. Do not copy `apply-patch` sources into `crates/patch` to -paper over a workspace dependency. +Update the pinned revision through a reviewed PR. The change can affect every adapter listed in [Codex reuse](codex-reuse.md), including process, filesystem, and network behavior. + + -Current pin: [upstream-lock.md](upstream-lock.md). -Product vs crate defaults: [behavior-differences.md](behavior-differences.md). -What may be reused besides apply-patch: [codex-reuse.md](codex-reuse.md). -NOTICE must keep the Apache-2.0 Codex attribution. +## Review the candidate -## Checklist +Choose an explicit tag or commit and record the reason. Inspect relevant upstream changes in patch handling, PTY, hardening, filesystem, sandbox, and proxy code. Check runtime and development dependencies separately; the presence of a development-only dependency is not proof that it enters the product binary. -1. Choose a **tag or commit** (not floating `main`). Record why. -2. `git submodule update --init third_party/codex` -3. `git -C third_party/codex fetch --tags` -4. `git -C third_party/codex checkout ` -5. Rebuild the isolated adapter: - `cargo test --manifest-path crates/patch/Cargo.toml` - `cargo clippy --manifest-path crates/patch/Cargo.toml --all-targets -- -D warnings` -6. Run `scripts/check-upstream-pin.sh` after updating the Commit cell in - `docs/upstream-lock.md` to the new SHA (the script fails if submodule - HEAD ≠ lock file). -7. Update [behavior-differences.md](behavior-differences.md) if apply - options, symlink policy, or parse errors changed. -8. Update [NOTICE](../NOTICE) if the reuse description or pin string - changed. -9. Judge the pin’s **execution subgraph** against - [codex-reuse.md](codex-reuse.md): cohesive execution vs agent / - model semantics vs Gateway allow bypass. Treat diffs in - process-hardening, PTY, UDS, filesystem, linux-sandbox, and - network-proxy as an execution/security changelog. Update the - candidate table. Do not add a Codex path dep to the root - workspace. Isolation stays in `crates/patch` and, when it exists, - `crates/codex-runtime` (`codespace-codex-runtime`). -10. Until that runtime workspace exists, the gate is SHA + patch - parity only. When it exists, also require: runtime adapter - compile, plus PTY / sandbox / process regressions. This work - package does not add that suite. -11. Open a PR. CI must run the pin check **and** `crates/patch` tests. - A red patch job is a failed deploy, not a warning. +Update the submodule, [pin record](upstream-lock.md), affected adapter locks, and attribution when needed. Preserve the separation between core types and Codex adapter types. Never copy a single upstream crate into the product to conceal an incompatible dependency. -There is **no** path that marks a failed parity run as success. + -## Local gate +## Validate before submission -```bash -./scripts/check-upstream-pin.sh -``` +1. Check the pin with `PIN_ONLY=1 ./scripts/check-upstream-pin.sh`. +2. Run `cargo fmt --check`, `cargo clippy --locked --all-targets -- -D warnings`, and tests for the root workspace and each isolated adapter: patch, codex-runtime, pty, file-system, linux-sandbox. +3. Build the patch, runtime, and Linux helper binaries; point integration tests at the intended binaries. Run workspace integration and protocol tests. +4. On Linux with bubblewrap and namespace support, run the sandbox isolation tests with `CODESPACE_REQUIRE_LINUX_SANDBOX=1`. Include restricted denial and enabled proxy behavior. +5. Check the dependency-policy scan and Runner's prohibited sandbox-helper library edges, as encoded in [CI](../.github/workflows/ci.yml). +6. Update behavior documentation and review both languages when defaults, errors, or limitations change. -Exit non-zero if the submodule SHA mismatches the lock file or if -`cargo test --manifest-path crates/patch/Cargo.toml` fails. +A passing patch subset is insufficient for an update that also affects execution adapters. Keep command outputs tied to the candidate SHA and report unavailable platform checks explicitly. The CI workflow is the authoritative executable list of current gates. -`PIN_ONLY=1 ./scripts/check-upstream-pin.sh` checks the SHA only. -CI runs that **before** fmt/clippy, then runs patch tests without -re-checking the SHA. Locally, the unprefixed script still does SHA + -`crates/patch` tests. + -## Forbidden +## Release and rollback -- File-copy vendor of `codex-rs/apply-patch` without its workspace - crates -- Wrapping the standalone `apply_patch` binary as the security boundary -- Silent `git apply` fallback -- Shipping when patch tests fail +Open a PR with the old/new SHA, behavioral changes, and test evidence. Do not merge a failed compatibility gate or silently fall back to another patch engine. If the update must be reverted, revert the submodule, adapter locks, required Cargo patches, and documentation together; then rerun the affected gates. A pin mismatch is an error, not a warning. diff --git a/scripts/check_docs.py b/scripts/check_docs.py index a4e3b28..b2ff44c 100644 --- a/scripts/check_docs.py +++ b/scripts/check_docs.py @@ -44,6 +44,9 @@ def headings(text): if marker and marker[1][0] == fence[0] and len(marker[1]) >= len(fence) and not marker[2].strip(): fence = None continue + anchor = re.fullmatch(r'', line.strip()) + if anchor: + found.append(anchor[1]) match = re.match(r'^#{1,6} (.+)$', line) if match: found.append(slug(match[1])) diff --git a/tests/adversarial-report.md b/tests/adversarial-report.md index 809fad5..a419fa5 100644 --- a/tests/adversarial-report.md +++ b/tests/adversarial-report.md @@ -1,30 +1,30 @@ -# W11 adversarial report +# Security and recovery coverage inventory -Scenarios from the original plan §12. `PASS` means an executable test in -this repo asserts the behavior. `UNVERIFIED` is in scope for honesty, not a -silent skip of a required gate. +This is a map of test sources, not a test-run report. Check the CI run for +the exact commit under review before claiming a scenario passed. Coverage +does not establish that every deployment has the same protections. -| Scenario | Result | Where | +| Scenario | Coverage | Where | | --- | --- | --- | -| Path escape `..` | PASS | `tests/security/adversarial.rs`, `crates/server/tests/read_find.rs` | -| Absolute path | PASS | `tests/security/adversarial.rs` | -| Symlink file | PASS | `tests/security/adversarial.rs`, `crates/runner` | -| Special file (FIFO) | PASS | `tests/security/adversarial.rs` | -| Context mismatch | PASS | `tests/security/adversarial.rs` | -| External edit version conflict | PASS | `tests/security/adversarial.rs` | -| Duplicate operation / key reuse | PASS | `tests/recovery/restart.rs`, `crates/server/tests/operations.rs` | -| Partial write failure is not `applied` | PASS | `tests/recovery/restart.rs`, `crates/server/tests/rollback.rs` | -| HTTP 401 / lost transport is not an operation | PASS | `tests/security/adversarial.rs`, `crates/server/tests/http_contract.rs` | -| Restart leaves unfinished as `unknown` | PASS | `tests/recovery/restart.rs`, `crates/store` | -| Shell + patch concurrent write | PASS | `tests/e2e/flow.rs`, `crates/server/tests/process.rs` | -| Invented `process_id` / `operation_id` | PASS | `tests/security/adversarial.rs`, `crates/server/tests/process.rs` | -| Output / time limits | PASS | `crates/server/tests/process.rs` | -| Bearer token not in error body | PASS | `tests/security/adversarial.rs`, `crates/server/src/logging.rs` | -| `workspace_info` → `read` → `apply_patch` → `exec_command` | PASS | `tests/e2e/flow.rs` | -| Shell isolation against `/etc/passwd` | UNVERIFIED | Gateway `exec_command` is host argv + workspace cwd, not a kernel sandbox. Linux runner spec is in `deploy/` / W05. | +| Path escape `..` | Test source | `tests/security/adversarial.rs`, `crates/server/tests/read_find.rs` | +| Absolute path | Test source | `tests/security/adversarial.rs` | +| Symlink file | Test source | `tests/security/adversarial.rs`, `crates/runner` | +| Special file (FIFO) | Test source | `tests/security/adversarial.rs` | +| Context mismatch | Test source | `tests/security/adversarial.rs` | +| External edit version conflict | Test source | `tests/security/adversarial.rs` | +| Duplicate operation / key reuse | Test source | `tests/recovery/restart.rs`, `crates/server/tests/operations.rs` | +| Partial write failure is not `applied` | Test source | `tests/recovery/restart.rs`, `crates/server/tests/rollback.rs` | +| HTTP 401 / lost transport is not an operation | Test source | `tests/security/adversarial.rs`, `crates/server/tests/http_contract.rs` | +| Restart leaves unfinished as `unknown` | Test source | `tests/recovery/restart.rs`, `crates/store` | +| Shell + patch concurrent write | Test source | `tests/e2e/flow.rs`, `crates/server/tests/process.rs` | +| Invented `process_id` / `operation_id` | Test source | `tests/security/adversarial.rs`, `crates/server/tests/process.rs` | +| Output / time limits | Test source | `crates/server/tests/process.rs` | +| Bearer token not in error body | Test source | `tests/security/adversarial.rs`, `crates/server/src/logging.rs` | +| `workspace_info` → `read` → `apply_patch` → `exec_command` | Test source | `tests/e2e/flow.rs` | +| Linux command and network isolation | Linux-specific test source | `crates/linux-sandbox/tests`, `crates/runner/tests/isolation_files.rs`; requires a usable helper and namespaces. See [runner isolation](../docs/runner-isolation.md). | | ChatGPT live-account adversarial calls | UNVERIFIED | No ChatGPT Custom Connector in this environment. | | Kernel/container escape | UNVERIFIED | Out of product scope. | -Required completion gates (path escape on file tools, partial failure -≠ `applied`, duplicate requests do not re-run, concurrent writes locked, -no Bearer in errors) are PASS. +Known recovery gap: post-apply verification errors can be recorded as +`rejected` after files changed, without entering snapshot restoration. See +[patch behavior](../docs/behavior-differences.md). diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 0fd65fc..7459b40 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -1,4 +1,4 @@ -# End-to-end tests (W11) +# End-to-end tests Executable coverage: `tests/e2e/flow.rs` (`cargo test -p codespace-server --test e2e`). diff --git a/tests/parity/README.md b/tests/parity/README.md index 6a0a5ef..bec402c 100644 --- a/tests/parity/README.md +++ b/tests/parity/README.md @@ -1,11 +1,11 @@ -# Parity fixtures (W06) +# Parity fixtures -These cases are copied from the pinned Codex apply-patch suite at +Selected cases exercise behavior from the pinned Codex apply-patch suite at `third_party/codex` (`rust-v0.154.0` / `6b9826e3aa83b1a5947db50f4332cb9c65f1b340`). They are a **subset**. Passing them does not mean the entire upstream suite ran. -| Fixture | Source | +| Case | Upstream reference | | --- | --- | | Add File `nested/new.txt` | `codex-rs/apply-patch/tests/suite/tool.rs` | diff --git a/tests/recovery/README.md b/tests/recovery/README.md index 38e1eb5..f92aff1 100644 --- a/tests/recovery/README.md +++ b/tests/recovery/README.md @@ -1,9 +1,9 @@ -# Recovery tests (W09-C / W11) +# Recovery tests Executable coverage: - `tests/recovery/restart.rs` (`cargo test -p codespace-server --test recovery`) -- `crates/server/src/rollback.rs` unit tests +- `crates/runner/src/rollback.rs` unit tests - `crates/server/tests/rollback.rs` MCP tests - `crates/store` unfinished-operation replay @@ -11,3 +11,7 @@ Rules: best-effort restore of file bytes, existence, and permission mode. Do **not** `git reset --hard`. Incomplete restore is `failed_partial`, never `applied`. Restart does not auto-apply `unknown` operations. Duplicate `operation_key` replays or conflicts; it does not re-run a new write. + +Snapshot restoration follows helper-apply failures. Post-apply verification +errors do not currently enter that restoration branch; see +[patch behavior and recovery](../../docs/behavior-differences.md). diff --git a/tests/security/README.md b/tests/security/README.md index 107a0e4..22ebb62 100644 --- a/tests/security/README.md +++ b/tests/security/README.md @@ -1,4 +1,4 @@ -# Security tests (W11) +# Security tests Executable coverage: `tests/security/adversarial.rs` (wired as `cargo test -p codespace-server --test security`). @@ -7,6 +7,7 @@ Covers path escape (`..`, absolute), symlink, FIFO, invented `process_id`, client `approved` claims, version conflict, context mismatch, and Bearer 401 bodies that must not echo the token. -Shell path sandboxing on the host gateway is **not** claimed. Linux -container isolation is specified in `deploy/` and -[docs/runner-isolation.md](../../docs/runner-isolation.md). +Command isolation depends on the Linux sandbox helper and its successful +probe. The Compose fixture is not a command backend. See +[runner isolation](../../docs/runner-isolation.md) for host fallback, +enabled-network failure behavior, and the Linux CI checks.