{"id":51415305,"url":"https://github.com/openclaw/turnwire","last_synced_at":"2026-07-08T23:00:36.754Z","repository":{"id":368965497,"uuid":"1287594240","full_name":"openclaw/turnwire","owner":"openclaw","description":"Audited text-only MCP relay.","archived":false,"fork":false,"pushed_at":"2026-07-05T18:19:30.000Z","size":230,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-06T21:19:33.439Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openclaw.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["moltbot"]}},"created_at":"2026-07-02T20:46:26.000Z","updated_at":"2026-07-06T10:51:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openclaw/turnwire","commit_stats":null,"previous_names":["openclaw/turnwire"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/openclaw/turnwire","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fturnwire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fturnwire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fturnwire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fturnwire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openclaw","download_url":"https://codeload.github.com/openclaw/turnwire/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Fturnwire/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35243953,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-04T19:00:21.185Z","updated_at":"2026-07-07T22:00:35.624Z","avatar_url":"https://github.com/openclaw.png","language":"Go","funding_links":["https://github.com/sponsors/moltbot"],"categories":[],"sub_categories":[],"readme":"# Turnwire\n\n[![CI](https://github.com/openclaw/turnwire/actions/workflows/ci.yml/badge.svg)](https://github.com/openclaw/turnwire/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nTurnwire is a signed, policy-guarded, text-only channel between two private\nenvironments. Each environment runs its own local endpoint. An agent carries a\nreleased envelope through OpenAI to the other endpoint; Turnwire never opens an\ninbound port and never gives OpenAI general host access.\n\nFive MCP tools:\n\n```text\nsend_message       guard text, sign a peer-addressed envelope\nreceive_message    verify peer signature, guard again, commit to inbox\nconfirm_delivery   verify and record the receiver acknowledgement\nlist_messages      read accepted messages; every read is audited\naudit_checkpoint   sign the current local audit-chain head\n```\n\nNo files, shell, browser, URL fetch, MCP resources/prompts, or model tools.\n\n## Practical flow\n\n```text\nwork agent / OpenAI                                      personal agent / OpenAI\n        |                                                           |\n        | Secure MCP Tunnel                         Secure MCP Tunnel|\n        v                                                           v\n work Turnwire -- local DLP -- GPT guard      GPT guard -- local DLP -- personal Turnwire\n        |                 |                         |                 |\n        |          signed envelope -- agent -------\u003e                 |\n        |                 \u003c---- signed acknowledgement               |\n        v                                                           v\n work audit.jsonl                                       personal audit.jsonl\n```\n\n1. `send_message` records the proposal locally, runs deterministic secret/DLP\n   checks, then calls the configured GPT guard with strict Structured Outputs.\n2. A denial produces no envelope. A review creates a local pending record;\n   only `turnwire approve MESSAGE_ID` can approve that exact body hash. MCP\n   cannot approve it.\n3. Allowed or locally approved text is signed with the source Ed25519 key. The\n   agent carries the JSON envelope to the peer's `receive_message` tool.\n4. The peer verifies destination, signature, body hash, age, and replay binding,\n   then independently repeats deterministic and GPT guards before committing.\n5. The peer commits the inbox entry, signs that exact audit head in an\n   acknowledgement, then logs receipt issuance. The source verifies and\n   records it with `confirm_delivery`.\n\nOpenAI does not “crawl” either machine. The tunnel client makes an outbound\nHTTPS connection, and OpenAI can call only the exposed MCP tools. Secure MCP\nTunnel is transport, not generic host-to-host networking.\n\n## Security model\n\n- Default guard: pinned `gpt-5.4-2026-03-05` through Responses.\n- GPT-5.5 supported only as pinned `gpt-5.5-2026-04-23`. Floating model\n  aliases are rejected.\n- Every releasable message gets an outbound model verdict. Every received\n  envelope gets a separate inbound verdict. Errors and malformed output fail\n  closed. The returned model must exactly match the configured snapshot.\n- Obvious credentials and secrets are denied locally before reaching the guard\n  API. GPT is a secondary classifier, not the cryptographic or deterministic\n  boundary.\n- Guard calls set `store: false`, `background: false`, provide no tools, and\n  demand strict JSON Schema output. GPT-5.4 defaults to `in_memory` prompt\n  caching. Current OpenAI docs say GPT-5.5 requires 24-hour extended caching,\n  so init selects `24h` for GPT-5.5.\n- Endpoints trust only configured peer public keys. Envelopes and delivery\n  acknowledgements are Ed25519 signed.\n- Exact text, decisions, policy/model, OpenAI response and `x-request-id`, peer\n  identities, hashes, and receipts enter a synced hash-chained local log.\n- Signed audit checkpoints can be independently stored to detect later\n  whole-log replacement or truncation.\n- MCP emits one structured result, rejects JSON-RPC batches, and caps every\n  input frame and output frame. Inbox reads stop at a fixed encoded-byte cap.\n- Per-process request and guard-call budgets bound request floods and model\n  spend. Exhaustion fails closed; counters reset when the process restarts.\n\nFor strongest OpenAI-side controls, use a dedicated API project approved for\nZero Data Retention. `store: false` alone does not remove default abuse-\nmonitoring retention. See [Data controls](https://developers.openai.com/api/docs/guides/your-data#v1responses).\n\nSecure MCP Tunnel does not emit individual transport requests as Compliance\nPlatform app events. Tunnel create/update/delete events appear in Platform\nAudit logs; normal custom-app invocation/auth logging applies separately. See\nthe official [logging boundaries](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels#logging-boundaries).\nTurnwire's local ledgers remain the authoritative per-message record.\n\n## Set up two endpoints\n\nRequirements: macOS or Linux, Go 1.25+, and `OPENAI_API_KEY`. Windows runtime\nfails closed until owner-only DACL enforcement exists.\n\nWork machine:\n\n```bash\ngo build -o ./bin/turnwire ./cmd/turnwire\n./bin/turnwire init --identity work\n./bin/turnwire identity\n```\n\nPersonal machine:\n\n```bash\ngo build -o ./bin/turnwire ./cmd/turnwire\n./bin/turnwire init --identity personal\n./bin/turnwire identity\n```\n\nExchange only the printed public keys, then pin each peer:\n\n```bash\n# work machine\n./bin/turnwire peer add personal PERSONAL_PUBLIC_KEY\n\n# personal machine\n./bin/turnwire peer add work WORK_PUBLIC_KEY\n```\n\nVerify both:\n\n```bash\n./bin/turnwire doctor --probe\n```\n\nGPT-5.5 example:\n\n```bash\n./bin/turnwire init --identity work --model gpt-5.5-2026-04-23 --force\n```\n\n`--force` preserves the key and audit history but replaces config. Recheck\npolicy and peers after a forced init.\n\n## Secure MCP Tunnel\n\nConfigure a separate tunnel and custom app per endpoint. The public\n[`tunnel-client`](https://github.com/openai/tunnel-client) can spawn Turnwire:\n\n```bash\ntunnel-client init \\\n  --sample sample_mcp_stdio_local \\\n  --profile turnwire-work \\\n  --tunnel-id TUNNEL_ID \\\n  --mcp-command \"/absolute/path/to/turnwire/bin/turnwire serve\"\n\ntunnel-client doctor --profile turnwire-work --explain\ntunnel-client run --profile turnwire-work\n```\n\nFollow the official [Secure MCP Tunnel guide](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels)\nfor permissions and workspace association. Associate a work endpoint with a\npersonal workspace only when the work administrator permits that relationship.\n\nDirect local MCP client:\n\n```json\n{\n  \"mcpServers\": {\n    \"turnwire-work\": {\n      \"command\": \"/absolute/path/to/turnwire/bin/turnwire\",\n      \"args\": [\"serve\"]\n    }\n  }\n}\n```\n\n## Review and audit\n\nOn `review_required`, approve locally, then retry the identical call:\n\n```bash\nturnwire approve MESSAGE_ID\n```\n\nAudit commands:\n\n```text\nturnwire log list [--type TYPE] [--limit N] [--json]\nturnwire log show ID [--json]\nturnwire log verify [--json]\nturnwire checkpoint\n```\n\nStore periodic checkpoints outside the endpoint. Reconcile message IDs, hashes,\nsigned acknowledgements, model/request IDs, and available OpenAI custom-app\ninvocation logs.\n\n## Important limits\n\n- If GPT inspects plaintext, OpenAI received it. Turnwire controls transfer\n  between environments; it cannot make guard input invisible to OpenAI.\n- A classifier cannot prove text non-confidential. Deterministic checks, narrow\n  policy, local review, signatures, and evidence reduce risk—not guarantee zero\n  leakage.\n- Tunnel authorization controls the app path. Stdio does not expose a verified\n  individual human caller identity; logs identify endpoint and peer.\n- A host administrator can replace binary, keys, policy, approvals, or log.\n- The agent carries released envelopes; Turnwire does not directly connect\n  hosts.\n- Text only. Attachments and arbitrary host access remain out of scope.\n\nRead the [threat model](docs/threat-model.md) and\n[configuration reference](docs/configuration.md) before deployment.\n\n## Development\n\n```bash\ngo test -race ./...\ngo vet ./...\ngo build ./cmd/turnwire\n```\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenclaw%2Fturnwire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenclaw%2Fturnwire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenclaw%2Fturnwire/lists"}