Codex Debug Bridge connects a personal messaging account to one live task in the macOS Codex App. It supports BlueBubbles iMessage, Telegram, Slack, WhatsApp Business Cloud API, and Microsoft Teams.
The main point is the execution surface. Incoming messages enter the same Codex App task that can use installed Codex App capabilities such as Browser and Computer Use. Every turn also receives a validated private seven-file authority bundle and can receive bounded Memory Forest route advice or local helper context.
Important
Version 0.3 is personal and single-owner. One instance belongs to one human and one dedicated Codex App task. Every enabled transport must bind exactly one owner and one conversation. It is not a shared bot, enterprise bridge, or multi-tenant service.
This project is experimental, self-hosted integration code. It is not affiliated with or endorsed by OpenAI, Apple, BlueBubbles, Telegram, Slack, Meta, or Microsoft.
The bridge submits into a selected live Codex App task through the app's loopback Chromium debug surface and reads the matching task result. It does not replace Codex with a standalone webhook model runtime.
- Browser can open and interact with websites inside Codex App's separate browser profile.
- Computer Use can observe and operate approved desktop apps after its plugin and macOS permissions are enabled.
- Skills, plugins, apps, connectors, model selection, approvals, and account policy remain part of the selected Codex App task.
The bridge does not implement or grant Browser or Computer Use. Current plugin, account, region, permission, and target-app state still decide whether they work. Browser is not available in Codex CLI or the Codex IDE extension. On macOS, Computer Use requires Screen Recording and Accessibility permissions.
Official documentation:
Before every incoming turn, the bridge validates and injects seven private Markdown files in a fixed order. A missing, empty, oversized, symlinked, unexpected, placeholder-bearing, or too-permissive file blocks submission before Codex.
The bundle separates durable responsibilities instead of relying on one old chat message.
| File | Responsibility |
|---|---|
AGENTS.md |
scope, precedence, change discipline, and verification |
SOUL.md |
voice and conversational behavior |
USER.md |
stable user preferences and time interpretation |
SOURCE_RULES.md |
evidence, freshness, uncertainty, and source handling |
TOOLS.md |
capability choice and side-effect boundaries |
IDENTITY.md |
assistant identity and orchestration role |
WORKFLOW_AUTO.md |
turn flow, continuity, and completion conditions |
This mechanical owner reduces instruction loss and cross-turn drift. It does not make a model infallible or turn context data into authority.
The included starter creates private XLTM, LTM, MTM, STM, and Daily layers. Its bundled helper returns bounded route metadata and provenance rather than raw memory bodies. Codex must open the routed canonical source before relying on it, and mutable facts still need fresh verification.
The starter is a public layout and retrieval method, not a hosted memory service, complete maintenance engine, or authorization system.
Mail, Calendar, location, Reminders, Contacts, selected documents, transcription, and user-owned memory can be added as read-only command helpers. The bridge passes only declared bounded fields, strips the inherited environment, limits time and bytes, and labels output as context data.
Transport credentials, authority files, helper execution, state, and attachments remain on the user's Mac. Model processing is not necessarily local. The assembled prompt is submitted through the signed-in Codex App and is governed by that account and model provider's data controls.
Depending on configuration, the prompt can contain message text, all seven authority bodies, attachment paths, audio transcripts, Memory Forest route output, and enabled helper output such as Mail, Calendar, document, or location context. Exact transport identifiers and absolute authority-file paths are excluded by default. Setting INCLUDE_TRANSPORT_IDENTIFIERS_IN_PROMPT=true intentionally includes exact identifiers.
Telegram, Slack, WhatsApp, and Teams requests are written to a private replayable inbox after authentication and allowlist checks. The inbox contains the normalized message text and reply-route metadata, not the provider token or raw webhook body. Once processing completes, the original inbox payload is atomically replaced by a bounded completion record. Review-mode Codex replies remain in a separate private pending file until confirmed delivery.
Review the applicable Codex account, organization, retention, privacy, and regional controls before connecting private sources. Do not describe this project as offline AI or a fully local model.
- BlueBubbles iMessage REST and webhook transport
- Telegram Bot API webhook transport
- Slack HTTP Events API transport
- WhatsApp Business Cloud API transport
- Microsoft Teams transport through the official Teams SDK
- exact single-owner and single-conversation bindings
- loopback-only operator API with generated bearer authentication
- provider-specific authentication and exact route checks
- bounded webhook bodies, outbound timeouts, and response limits
- replayable private cloud-transport inbox with hashed event keys
- ordered delivery into one selected Codex App task
- restart-safe review queue with authenticated pending delivery
- full mode with explicit automatic delivery
- seven-file private authority initialization and validation
- optional read-only helper manifest
- Memory Forest starter and route-only helper
- bounded BlueBubbles attachment downloads
- safe-by-default URL handling with previews off
- exact task-result tailing and generated attachment extraction
Not implemented:
- multi-user or enterprise identity and task isolation
- a hosted control plane
- cross-user filesystem access or relay
- cloud-transport media ingestion in Telegram, Slack, WhatsApp, or Teams
- automatic installation or permission grants for Codex App capabilities
- a complete private authority compiler, evaluation corpus, or memory-maintenance service
flowchart LR
owner["One allowlisted owner"] --> provider["Enabled message transport"]
provider --> ingress["Provider auth, owner and conversation binding"]
ingress --> kind{"Cloud webhook"}
kind -->|"Telegram, Slack, WhatsApp, Teams"| durable["Private replayable cloud inbox"]
kind -->|"BlueBubbles"| queue
durable --> queue["Ordered personal bridge queue"]
authority["Seven private authority files"] --> prompt["Prompt assembly"]
memory["Optional Memory Forest route"] --> prompt
helpers["Optional bounded helpers"] --> prompt
queue --> prompt
prompt --> task["One selected Codex App task"]
task --> tools["Browser, Computer Use, skills, plugins, apps"]
task --> result["Matching task result"]
result --> mode{"review or full"}
mode --> pending["Pending review"]
mode --> delivery["Origin transport delivery"]
pending --> delivery
delivery --> owner
- macOS
- Node.js 22 or newer
- macOS Codex App, signed in to the intended account
- one dedicated Codex App task
- credentials for at least one supported message transport
- a safe self-chat or dedicated personal test conversation
BlueBubbles additionally requires BlueBubbles Server and an iMessage account. Telegram, Slack, WhatsApp, and Teams need an HTTPS callback that forwards only the selected webhook route to the loopback bridge.
git clone https://github.com/hyungchulc/codex-debug-bridge.git
cd codex-debug-bridge
npm ci
cp .env.example .env
chmod 600 .envGenerate a separate webhook secret.
openssl rand -base64 32Set the minimum BlueBubbles and personal binding values in .env.
BRIDGE_TRUST_PROFILE=review
BRIDGE_AUTO_SEND=false
BRIDGE_AUTHORITY_DIR=./.bridge-authority
BLUEBUBBLES_ENABLED=true
BLUEBUBBLES_BASE_URL=http://127.0.0.1:1234
BLUEBUBBLES_PASSWORD=YOUR_BLUEBUBBLES_PASSWORD
BLUEBUBBLES_WEBHOOK_SECRET=YOUR_SEPARATE_RANDOM_SECRET
ALLOWED_CHAT_GUIDS=ONE_EXACT_CHAT_GUID
ALLOWED_HANDLES=ONE_EXACT_OWNER_HANDLEBoth BlueBubbles allowlists are required. The chat GUID binds one conversation and the handle binds one owner in that conversation. Startup fails if either is missing or contains more than one value.
Interactive setup:
npm run setup:authority -- --output ./.bridge-authorityNon-interactive setup uses a private JSON file rather than inline shell JSON.
cp examples/authority/answers.example.json authority-answers.local.json
chmod 600 authority-answers.local.json
npm run setup:authority -- \
--output ./.bridge-authority \
--answers authority-answers.local.jsonReview all seven files, then validate.
npm run validate:authority -- ./.bridge-authorityThe directory must be 0700; each file must be a real regular file with mode 0600.
Open one dedicated task. For deterministic selection, configure exactly one of an exact task ID or a unique pinned title. The task ID is recommended.
CODEX_PREFERRED_THREAD_ID=
CODEX_PREFERRED_THREAD_TITLE=Codex Debug Bridge
CODEX_PREFERRED_THREAD_TIMEOUT_MS=60000Startup fails if both selectors are blank or both are set. npm run probe:codex verifies that the selected task exists and is active without sending a prompt, and every prompt or steer submission rechecks the same binding before it can touch the composer.
Leave model and reasoning expectations blank to accept the current task state.
CODEX_READY_MODEL_TEXT=
CODEX_READY_REASONING_TEXT=Install and enable Browser or Computer Use inside Codex App only if needed. Keep the debug port on loopback.
scripts/open-codex-debug.sh
npm run probe:codex
npm run probe:bluebubbles
npm startOr run the combined launcher.
scripts/start-all.shSubscribe a New Messages webhook to:
http://127.0.0.1:3099/webhook/bluebubbles?token=YOUR_SEPARATE_RANDOM_SECRET
If custom headers are supported, use x-bridge-webhook-token and omit the query token. Do not reuse the BlueBubbles REST password as the webhook secret.
- Send from the exact allowed handle into the exact allowed chat.
- Confirm a pending reply is created and nothing is sent automatically.
- Inspect authenticated state.
node scripts/operator-request.js /health/details
node scripts/operator-request.js /pending- Deliver the latest pending result after review.
scripts/send-latest.sh- Confirm the message appears once in the exact target chat.
- Confirm the wrong sender in the same chat and the same sender in another chat are ignored.
BRIDGE_TRUST_PROFILE=full
BRIDGE_AUTO_SEND=trueRestart and repeat the exact-route test. Full mode permits automatic transport delivery. It does not bypass provider authentication, owner or conversation bindings, deduplication, attachment limits, Codex approvals, macOS permissions, or authority.
The same bridge core supports Telegram, Slack, WhatsApp Business Cloud API, and Microsoft Teams. Each needs provider credentials, one owner, one conversation, and its own webhook path.
When BlueBubbles is not used, set BLUEBUBBLES_ENABLED=false. Cloud transports in version 0.3 are text-only. Attachments and media are not downloaded from Telegram, Slack, WhatsApp, or Teams.
Complete setup, authentication behavior, HTTPS reverse-proxy rules, and environment fields are in Messaging transports.
The bridge remains bound to loopback. A reverse proxy or authenticated tunnel may publish only one or more of these paths.
/webhook/telegram
/webhook/slack
/webhook/whatsapp
/webhook/teams
Preserve exact raw request bytes and provider authentication headers, rewrite Host to the configured loopback bridge host and port, and never expose /ask, /pending, /transport-inbox/*, /health/details, /bluebubbles/*, the BlueBubbles REST port, or the Codex debug port.
review queues generated transport replies in PENDING_REPLY_PATH and blocks direct bridge mutation endpoints. The queue survives a normal bridge restart. A confirmed pending-send request remains the deliberate delivery action.
If the process stops after an external provider accepted a send but before the local pending file is updated, the delivery result is ambiguous. Inspect the target conversation before confirming another send.
It does not isolate Codex from the rest of the same macOS account. Shell, Computer Use, Browser, filesystem, Keychain, Automation, and app permissions can provide other side-effect paths. Keep Codex approvals and macOS permissions narrow, and use a separate macOS account or machine when a real principal boundary is needed.
Create a private manifest.
cp examples/helpers/manifest.example.json helpers.local.json
chmod 600 helpers.local.jsonEnable it in .env.
CONTEXT_HELPERS_ENABLED=true
CONTEXT_HELPERS_MANIFEST=./helpers.local.json
CONTEXT_HELPERS_TIMEOUT_MS=2500
CONTEXT_HELPERS_MAX_BYTES=65536Commands must use absolute executable paths. Helpers receive only declared fields and return provenance-bearing JSON. Required helper failure blocks the turn; optional failure produces a short unavailable section. Successful helper text enters the Codex prompt.
See Helper contract.
Initialize a private forest.
node skills/memory-forest-starter/scripts/init_memory_forest.mjs \
--output ./.memory-forestAdd the route helper to the private helper manifest. JSON does not expand shell variables, so use real absolute paths.
{
"id": "memory",
"transport": "command",
"command": "/ABSOLUTE/REPOSITORY/PATH/skills/memory-forest-starter/scripts/memory_route_helper.mjs",
"args": ["--root", "/ABSOLUTE/PRIVATE/MEMORY/ROOT"],
"required": false,
"timeoutMs": 2500,
"maxBytes": 65536,
"inputFields": ["query"]
}The helper returns relative routes without source bodies. Record the private root in private authority or a private runbook available to the selected Codex task.
The standard route needs only BlueBubbles Server's REST API and New Messages webhook. BlueBubbles Private API is not required.
Optional exact-message read or played receipts and richer metadata can use a separately maintained, pinned server and helper pair. Private API installation can require disabling System Integrity Protection and Library Validation, which materially weakens macOS protections.
See BlueBubbles integration for the standard setup, exact inspected fork commits, pairing rule, and official warnings.
Never commit or publish these materials.
| Material | Protection |
|---|---|
.env |
real file, mode 0600 |
| authority directory | real directory 0700, seven files 0600 |
| generated operator token | mode 0600 |
| inbox, pending, job, message-index, attachment, transcript, and preview files | private directories 0700, files 0600 |
| helper manifest | real file, mode 0600 |
| Memory Forest | private local root |
| transport passwords, bot tokens, app secrets, and signing secrets | never logged or committed |
| Codex config, task history, login, and screenshots | private to the owning account |
| Mail, Calendar, location, Messages, Contacts, transcripts, and documents | never copied into this repository |
Startup loads .env before runtime modules, rejects symlinks or permissive modes, repairs bridge-owned private state modes, and rejects symlink state targets.
First start creates a private bearer token at BRIDGE_AUTH_TOKEN_FILE. The wrapper reads it without putting the token in command arguments.
node scripts/operator-request.js /health/details
node scripts/operator-request.js /pending
node scripts/operator-request.js /transport-inbox/attentionImportant routes:
GET /healthandGET /ready, minimal loopback statusGET /health/details, authenticated component statusPOST /ask, authenticated direct Codex task submission- provider webhook paths under
/webhook/ GET /pending, review queuePOST /pending/latest/sendandPOST /pending/:id/sendGET /transport-inbox/attention, cloud events claimed before an interrupted Codex runPOST /transport-inbox/:key/retry, explicit confirmed recovery for one claimed cloud event- authenticated, exact-destination-bound BlueBubbles text, attachment, reaction, read, played, and voice routes
- authenticated Codex task-relay control in full auto-send mode
Direct FaceTime operator routes are disabled in this public personal release. The remaining BlueBubbles operator routes fail closed when the configured chat changes or an exact message or attachment was not previously observed on the allowed route.
Inbound Codex model and reasoning commands are disabled by default and should stay disabled unless the one owner explicitly needs them.
CODEX_CONTROL_COMMANDS_ENABLED=falseCloud events are acknowledged only after their private inbox admission has been flushed. An event left in admitted state is replayed automatically after restart. An event left in claimed state is never replayed blindly because Codex may already have acted on it. Inspect the selected task and pending queue first, then retry one exact item only when needed.
node scripts/operator-request.js /transport-inbox/attention
node scripts/operator-request.js \
/transport-inbox/ONE_64_CHARACTER_KEY/retry \
'{"confirm":true}'BlueBubbles currently keeps its inbound retry cache in process memory for ten minutes. After a bridge restart, inspect the selected Codex task before resending the same iMessage webhook payload. The durable inbox lifecycle above applies to Telegram, Slack, WhatsApp, and Teams.
BlueBubbles attachments stream into private files under a default 25 MiB cap.
INCOMING_ATTACHMENT_MAX_BYTES=26214400Manual downloads choose a managed destination under ATTACHMENT_DIR; callers cannot supply an arbitrary output path. Manual attachment send, voice send, and audio transcription accept only bounded real regular non-symlink files under that same root. Generated Codex attachments are accepted only from the two documented generated-image roots, and generated rich-link assets only from RICH_LINK_ASSET_DIR.
External audio transcription is off by default. When enabled, downloaded audio is sent to the configured OpenAI-compatible transcription endpoint.
AUDIO_TRANSCRIPTION_ENABLED=false
OPENAI_API_KEY=Generated link previews are off by default. The guarded generator rejects private, loopback, link-local, reserved, metadata, and unsafe redirect targets and enforces byte caps.
BLUEBUBBLES_LINK_PREVIEW_MODE=offKeep previews off unless the exact route has been tested.
| Path | Purpose |
|---|---|
src/server.js |
secure bootstrap that loads .env before runtime imports |
src/server-runtime.js |
HTTP routes, ordering, prompt submission, pending and automatic delivery |
src/env.js |
private configuration and fail-closed validation |
src/bridge-auth.js |
loopback, Host, webhook, and operator perimeter |
src/transports/ |
Telegram, Slack, WhatsApp, and Teams adapters |
src/transport-inbox.js |
replayable private cloud-message inbox and explicit retry state |
src/transport-replies.js |
originating cloud-transport reply routing and attachment fail-closed guard |
src/pending.js |
private restart-safe review queue |
src/bluebubbles.js |
BlueBubbles REST client and bounded downloads |
src/bluebubbles-route.js |
exact persisted and operator destination checks |
src/attachment-path.js |
regular-file, root, symlink, and byte-limit enforcement |
src/generated-rich-link.js |
exact rich-link destination and private asset-root guard |
src/codex-cdp.js |
Codex App debug connection, task selection, and submission |
src/session-log.js |
matching task-result and generated-attachment extraction |
src/authority-files.js |
seven-file validation and canonical order |
src/incoming-prompt.js |
authority, context, and current-message assembly |
src/context-helpers.js |
bounded local command-helper contract |
src/incoming-jobs.js |
bounded metadata-only recovery ledger |
src/private-files.js |
private bridge-owned directory and file enforcement |
templates/authority/ |
generic authority templates |
skills/bridge-authority-bootstrap/ |
authority onboarding skill |
skills/memory-forest-starter/ |
Memory Forest layout and route helper |
examples/launchagent/local.codex-debug-bridge.plist |
user LaunchAgent template with no embedded secrets |
docs/ |
architecture, installation, transports, helpers, and BlueBubbles details |
Run the complete local gate.
npm run check
npm pack --dry-run
npm audit
git diff --checkThe release audit inspects the working tree and package for private runtime files, symlinks, macOS home paths, consumer email addresses, phone numbers, coordinates, key material, and common live-secret forms.
After an update, return to review mode and repeat one exact owner and conversation smoke test before automatic delivery.
The connector design was reviewed against OpenClaw and Hermes because both expose useful open-source channel patterns. OpenClaw and Hermes are MIT-licensed. This repository implements its adapters from provider protocols and official SDK behavior rather than copying their connector source, so it does not vendor either project.
Audited upstream snapshots:
- OpenClaw commit
b8359a78e75ae3c08eeb35dfe62da215886c532d - Hermes commit
8208fc52701332f213e6c51ebc0b610be00300de
If future work copies substantial upstream code, preserve its exact license and copyright notice in a THIRD_PARTY_NOTICES file.
This repository intentionally stops at a personal bridge. A company product needs a different control plane with authenticated employee principals, separate tasks or sessions, tenant isolation, policy, approvals, audit, scheduling, and verified effect receipts. It should not grow from this release by merely pointing one Codex App task at a shared Slack or Teams channel.
- Architecture
- Installation
- Messaging transports
- BlueBubbles integration
- Context helpers
- Security policy
MIT