codeharbor
v0.1.23
Published
Instant-messaging bridge for Codex CLI sessions
Downloads
1,778
Readme
CodeHarbor
CodeHarbor is an instant-messaging bridge for codex CLI.
Users send messages in Matrix, CodeHarbor routes each message to a Codex session, then sends the final result back to the same Matrix room.
What It Does
- Matrix channel adapter (receive + reply)
- Session-to-Codex mapping via persistent SQLite state
- One-time migration import from legacy
state.json - Duplicate Matrix event protection
- Context-aware trigger (DM direct chat + group mention/reply + active session window)
- Room-level trigger policy overrides
- Real
/stopcancellation (kills in-flight Codex process) - Session runtime workers (logical worker per
channel:room:user, with worker stats in/status) - Rate limiting + concurrency guardrails (user/room/global)
- Progress + typing updates with group notice coalescing (
m.replaceedit) - CLI-compat mode (
cli_compat_mode) for minimal prompt rewriting + raw event passthrough - Attachment metadata passthrough from Matrix events into prompt context
- Request observability (request_id, queue/exec/send durations, status counters)
- NPM-distributed CLI (
codeharbor)
Architecture
Matrix Room -> MatrixChannel -> Orchestrator -> CodexExecutor (codex exec/resume)
|
-> StateStore (SQLite)Implementation Status
- Primary runtime: TypeScript/Node (
src/,dist/,npm run ...) - Legacy/reference implementation: Python (
app/,tests/) - New features and fixes target the TypeScript runtime.
- Python code is kept as legacy reference only (maintenance mode).
Prerequisites
- Node.js 22+
codexCLI installed and authenticated (codex login)- A Matrix bot user + access token
Install
Install globally from npm (after publish):
npm install -g codeharborLinux one-command install (creates /opt/codeharbor, sets ownership, installs latest package):
curl -fsSL https://raw.githubusercontent.com/biglone/CodeHarbor/main/scripts/install-linux.sh | bashLinux easy mode (install + write .env + enable/start systemd in one run):
curl -fsSL https://raw.githubusercontent.com/biglone/CodeHarbor/main/scripts/install-linux-easy.sh | bash -s -- \
--matrix-homeserver https://matrix.example.com \
--matrix-user-id @bot:example.com \
--matrix-access-token 'your-token'Install first, then enable systemd service with one command:
codeharbor service installInstall + enable main and admin services:
codeharbor service install --with-adminRestart installed service(s):
codeharbor service restart --with-adminRemove installed services:
codeharbor service uninstall --with-adminNotes:
- Service commands auto-elevate with
sudowhen root privileges are required. codeharbor service install --with-adminandinstall-linux-easy.sh --enable-admin-servicenow install/etc/sudoers.d/codeharbor-restartfor non-root service users, so Admin UI restart actions work out-of-box.npm install -g codeharbor@latestnow performs best-effort restart for activecodeharbor(.service)units on Linux so upgrades take effect immediately (setCODEHARBOR_SKIP_POSTINSTALL_RESTART=1to disable).- If your environment blocks interactive
sudo, use explicit fallback:sudo <node-bin> <codeharbor-cli-script> service ...
Enable Admin service at install time:
curl -fsSL https://raw.githubusercontent.com/biglone/CodeHarbor/main/scripts/install-linux-easy.sh | bash -s -- \
--matrix-homeserver https://matrix.example.com \
--matrix-user-id @bot:example.com \
--matrix-access-token 'your-token' \
--enable-admin-service \
--admin-token 'replace-with-strong-token'Run local script with custom options:
./scripts/install-linux.sh --app-dir /srv/codeharbor --package [email protected] --initRuntime home behavior:
- By default, all
codeharborcommands use~/.codeharborfor.envand relative data paths. - Backward compatibility: if
/opt/codeharbor/.envalready exists, it continues to be used automatically. - No manual
cdis required after installation. - To use a custom runtime directory, set
CODEHARBOR_HOME(for exampleexport CODEHARBOR_HOME=/srv/codeharbor).
Install directly from GitHub:
npm install -g github:biglone/CodeHarborBuild a local package tarball and install it:
npm pack
npm install -g ./codeharbor-<version>.tgzGitHub CI/CD Publish
CodeHarbor supports auto publish to npm from GitHub Actions.
Setup once:
- Configure npm Trusted Publishing for this repository/workflow (preferred):
- npm package settings -> Trusted publishing -> Add publisher
- Provider: GitHub Actions
- Repository:
biglone/CodeHarbor - Workflow file:
.github/workflows/release-npm.yml
- Optional fallback: set repository secret
NPM_TOKEN(npm automation token). - Push to
mainwith a publish trigger commit message.
Trigger rules:
pushtomain+ commit message includes[publish-npm]-> run publish workflowpushtomain+ commit message includes bothreleaseand a semver version -> run publish workflow- examples:
release v0.1.1,chore: release 0.1.2
- examples:
workflow_dispatch-> manual publish from GitHub Actions UI
The workflow runs typecheck, test, test:e2e (Admin UI Playwright), build, node dist/cli.js --help, npm pack --dry-run, then publishes with:
npm publish --provenance --access publicAuth mode selection:
- If
NPM_TOKENsecret exists: publish with token. - If
NPM_TOKENis absent: publish via npm Trusted Publishing (OIDC).
If the same package version already exists on npm, publish is skipped automatically.
Release checklist (recommended):
- Update
CHANGELOG.mdwith a new version section and bullet-point release notes. - Update version in
package.json(npm version patch|minor|major). - Validate changelog entry:
npm run changelog:check
- Push to
mainwith[publish-npm]orrelease vX.Y.Zin commit message. - Verify workflow status in GitHub Actions.
- Verify package on npm:
npm view codeharbor versionRun e2e locally:
npm run test:e2eIf your machine has no system Chrome, run:
PLAYWRIGHT_USE_SYSTEM_CHROME=false npm run e2e:install
PLAYWRIGHT_USE_SYSTEM_CHROME=false npm run test:e2ePlanning Docs
REQUIREMENTS.md: current baseline + next-stage requirementsTASK_LIST.md: implementation task breakdown and statusdocs/CONFIG_UI_DESIGN.md: configuration UI MVP designdocs/CONFIG_CATALOG.md: consolidated configuration matrix (required/runtime/UI/effective timing)docs/ADMIN_STANDALONE_DEPLOY.md: standalone admin deployment and Cloudflare Tunnel exposure guidedocs/BACKUP_AUTOMATION.md: scheduled config backup and restore operationsdocs/RELEASE.md: release process and CI/publish policy
Quick Start
For local development from source:
- Install dependencies:
npm install- Configure environment:
export CODEHARBOR_HOME="$(pwd)"
codeharbor initRequired values:
MATRIX_HOMESERVERMATRIX_USER_IDMATRIX_ACCESS_TOKEN
- Run in dev mode:
export CODEHARBOR_HOME="$(pwd)"
npm run dev- Build and run as CLI:
npm run build
export CODEHARBOR_HOME="$(pwd)"
node dist/cli.js startConfiguration Baseline
Use this layered reference to avoid mixing boot-only and runtime tuning items:
It documents:
- which keys are required vs optional
- which keys can be edited in Admin UI
- whether changes are immediate or restart-scoped
- recommended profiles for local/internal/public deployment
Commands
codeharbor init: guided setup for.env(supports--forceto overwrite directly)codeharbor start: start servicecodeharbor doctor: checkcodexand Matrix connectivitycodeharbor admin serve: start admin UI + config API servercodeharbor service install: install/enable systemd unit(s) after npm install (supports--with-admin)codeharbor service restart: restart installed systemd unit(s) (supports--with-admin)codeharbor service uninstall: remove installed systemd unit(s) (supports--with-admin)codeharbor config export: export current config snapshot as JSONcodeharbor config import <file>: import config snapshot JSON (supports--dry-run)npm run changelog:check: validateCHANGELOG.mdhas notes for current package versionscripts/install-linux.sh: Linux bootstrap installer (creates runtime dir + installs npm package)scripts/install-linux-easy.sh: one-shot Linux install + config + systemd auto-startscripts/backup-config.sh: export timestamped snapshot and keep latest N backupsscripts/install-backup-timer.sh: install/update user-level systemd timer for automatic backupsnpm run test:e2e: run Admin UI end-to-end tests (Playwright)
Config Backup Script
Create a timestamped snapshot in backups/config and keep latest 20 by default:
./scripts/backup-config.shCustom directory and retention:
./scripts/backup-config.sh --dir /var/backups/codeharbor --keep 30Install/update automatic backup timer:
./scripts/install-backup-timer.sh --schedule "*-*-* 03:30:00" --dir /var/backups/codeharbor --keep 30Full guide:
Admin UI And API
Start server:
codeharbor admin serveOptional overrides:
codeharbor admin serve --host 127.0.0.1 --port 8787If you bind Admin to a non-loopback host and both ADMIN_TOKEN and ADMIN_TOKENS_JSON are empty, startup is rejected by default.
Explicit bypass exists but is not recommended:
codeharbor admin serve --host 0.0.0.0 --allow-insecure-no-tokenOpen these UI routes in browser:
/or/settings/global/settings/rooms/health/audit
Main endpoints:
GET /api/admin/auth/statusGET /api/admin/config/globalPUT /api/admin/config/globalGET /api/admin/config/roomsGET /api/admin/config/rooms/:roomIdPUT /api/admin/config/rooms/:roomIdDELETE /api/admin/config/rooms/:roomIdGET /api/admin/healthGET /api/admin/audit?limit=50
When ADMIN_TOKEN or ADMIN_TOKENS_JSON is set, requests must include:
Authorization: Bearer <ADMIN_TOKEN>Access control options:
ADMIN_TOKEN: require bearer token for/api/admin/*ADMIN_TOKENS_JSON: optional multi-token RBAC list (supportsadminandviewerroles)ADMIN_IP_ALLOWLIST: optional comma-separated client IP whitelist (for example127.0.0.1,192.168.1.10)ADMIN_ALLOWED_ORIGINS: optional CORS origin allowlist for browser-based cross-origin admin access
RBAC behavior:
viewertokens can call read endpoints (GET /api/admin/*)admintokens can call read + write endpoints (PUT/POST/DELETE /api/admin/*)- for
ADMIN_TOKENS_JSON, audit actor is derived from token identity (actorfield), notx-admin-actor - Admin UI shows current permission status (role/source) after saving auth
Rotate tokens quickly (repository script):
./scripts/rotate-admin-token.sh --target rbac --role admin --actor ops-admin
./scripts/rotate-admin-token.sh --target rbac --role viewer --actor ops-auditNote: PUT /api/admin/config/global writes to .env and marks changes as restart-required.
Admin UI Quick Walkthrough
- Start server:
codeharbor admin serve. - Open
/settings/global, setAdmin Token(if enabled), then clickSave Auth. - Adjust global fields and click
Save Global Config(UI shows restart-required warning). - Use
Restart Main ServiceorRestart Main + Adminbuttons for one-click restart from Admin UI. If services were installed with--with-admin, restart permissions are auto-configured by installer. - Open
/settings/rooms, fillRoom ID + Workdir, thenSave Room. - Open
/healthto run connectivity checks (codex+ Matrix). - Open
/auditto verify config revisions (actor/summary/payload).
Standalone Admin Deployment
codeharbor admin serve can run as an independent service on target servers without browser/desktop.
Access can come from your local browser through a gateway (for example Cloudflare Tunnel).
See:
Startup Preflight
Before codeharbor start and codeharbor doctor, CodeHarbor runs a preflight check for:
- required Matrix env vars
CODEX_BINavailabilityCODEX_WORKDIRvalidity.envpresence warning
If any check fails, it prints actionable fix commands (for example codeharbor init).
Message Rules
- Direct Message (DM)
- all text messages are processed by default (no prefix required)
- Group Room
- when
GROUP_DIRECT_MODE_ENABLED=true, all non-empty messages are processed directly (no prefix/mention/reply required) - processed when any allowed trigger matches:
- message mentions bot user id
- message replies to a bot message
- sender has an active conversation window
- optional explicit prefix match (
MATRIX_COMMAND_PREFIX)
- when
- Trigger Policy
GROUP_DIRECT_MODE_ENABLEDcontrols whether groups bypass trigger matching entirely- global defaults via
GROUP_TRIGGER_ALLOW_* - per-room overrides via
ROOM_TRIGGER_POLICY_JSON
- Active Conversation Window
- each accepted request activates the sender's conversation in that room
- activation TTL:
SESSION_ACTIVE_WINDOW_MINUTES(default:20)
- Control commands
/statusshow session + limiter + metrics + runtime worker status/resetclear bound Codex session and keep conversation active/stopcancel in-flight execution (if running) and reset session context/agents statusshow multi-agent workflow status for current session (when enabled)/agents run <objective>run Planner -> Executor -> Reviewer workflow (when enabled)/autodev statusshow AutoDev doc/task summary + run snapshot (when enabled)/autodev run [taskId]auto-pick pending task (or run specified task) fromTASK_LIST.md(when enabled)
Multi-Agent Workflow (Phase B, Opt-In)
AGENT_WORKFLOW_ENABLED=true- enable
/agentsand/autodevworkflow commands
- enable
AGENT_WORKFLOW_AUTO_REPAIR_MAX_ROUNDS- reviewer reject loop upper bound (default
1)
- reviewer reject loop upper bound (default
AutoDev (/autodev) conventions:
- Workspace must contain
REQUIREMENTS.mdandTASK_LIST.md. TASK_LIST.mdshould include task IDs and status markers (⬜,🔄,✅,❌,🚫) in table rows or checklist rows./autodev runselects🔄task first, then⬜task.- When reviewer verdict is
APPROVED, CodeHarbor updates the task status to✅automatically.
Default is disabled to keep legacy behavior unchanged.
CLI Compatibility Mode
To make IM behavior closer to local codex CLI interaction, enable:
CLI_COMPAT_MODE=true- preserve user prompt whitespace
- avoid stripping
@botmention text in prompt body - enable richer raw event passthrough summaries
CLI_COMPAT_PASSTHROUGH_EVENTS=true- emit raw event summaries from codex JSON stream
CLI_COMPAT_PRESERVE_WHITESPACE=true- keep incoming Matrix message body untrimmed for execution
CLI_COMPAT_DISABLE_REPLY_CHUNK_SPLIT=true|false- optionally send one full message chunk to Matrix without auto split
CLI_COMPAT_PROGRESS_THROTTLE_MS- lower update throttle for near-real-time progress
CLI_COMPAT_FETCH_MEDIA=true|false- download Matrix
mxc://media (image) to temp file and pass it to codex via--image
- download Matrix
CLI_COMPAT_TRANSCRIBE_AUDIO=true|false- download Matrix
m.audioattachments and transcribe them into prompt context
- download Matrix
CLI_COMPAT_AUDIO_TRANSCRIBE_MODEL- OpenAI transcription model (default
gpt-4o-mini-transcribe)
- OpenAI transcription model (default
CLI_COMPAT_AUDIO_TRANSCRIBE_TIMEOUT_MS- timeout for each audio transcription request
CLI_COMPAT_AUDIO_TRANSCRIBE_MAX_CHARS- max transcript length appended to prompt for one attachment
CLI_COMPAT_AUDIO_TRANSCRIBE_MAX_RETRIES- retry count for local/OpenAI transcription failures (default
1)
- retry count for local/OpenAI transcription failures (default
CLI_COMPAT_AUDIO_TRANSCRIBE_RETRY_DELAY_MS- base retry delay between attempts
CLI_COMPAT_AUDIO_TRANSCRIBE_MAX_BYTES- skip transcription when attachment is larger than this size
CLI_COMPAT_AUDIO_LOCAL_WHISPER_COMMAND- optional local whisper command template (use
{input}placeholder for audio file path) - helper command shipped by package:
codeharbor-whisper-transcribe --input {input} --model small
- optional local whisper command template (use
CLI_COMPAT_AUDIO_LOCAL_WHISPER_TIMEOUT_MS- timeout for local whisper command execution
CLI_COMPAT_RECORD_PATH=/abs/path/records.jsonl- append executed prompts as JSONL for replay benchmarking
Note: execution still uses codex exec/resume per request; compatibility mode focuses on behavior parity and reduced middleware interference.
Persistence
STATE_DB_PATH=data/state.db- SQLite store for sessions + processed event ids
STATE_PATH=data/state.json- legacy JSON source for one-time migration import when SQLite is empty
MAX_SESSION_AGE_DAYS=30- prune stale sessions by age
MAX_SESSIONS=5000- prune least-recently-updated sessions when over limit
Rate Limiting
RATE_LIMIT_WINDOW_SECONDSRATE_LIMIT_MAX_REQUESTS_PER_USERRATE_LIMIT_MAX_REQUESTS_PER_ROOMRATE_LIMIT_MAX_CONCURRENT_GLOBALRATE_LIMIT_MAX_CONCURRENT_PER_USERRATE_LIMIT_MAX_CONCURRENT_PER_ROOM
Set a value to 0 to disable a specific limiter.
Codex CLI Alignment
Use these to align runtime with your terminal CLI profile:
CODEX_SANDBOX_MODECODEX_APPROVAL_POLICYCODEX_EXTRA_ARGSCODEX_EXTRA_ENV_JSON
When image attachments are present and CLI_COMPAT_FETCH_MEDIA=true, CodeHarbor will:
- download
mxc://media to a temp file - pass local file paths as
--imageto codex exec - best-effort cleanup temp files after the request
- optional prompt record append (
CLI_COMPAT_RECORD_PATH) for deterministic replay input
When audio attachments are present and both CLI_COMPAT_FETCH_MEDIA=true and CLI_COMPAT_TRANSCRIBE_AUDIO=true, CodeHarbor will:
- download
m.audiomedia to a temp file - skip oversized audio files based on
CLI_COMPAT_AUDIO_TRANSCRIBE_MAX_BYTES - if
CLI_COMPAT_AUDIO_LOCAL_WHISPER_COMMANDis configured, execute local whisper first - if local whisper fails and
OPENAI_API_KEYis available, fallback to OpenAI transcription API - retry transient failures using
CLI_COMPAT_AUDIO_TRANSCRIBE_MAX_RETRIES - append transcript to
[audio_transcripts]prompt block - continue request even if transcription fails (warn log + no transcript)
- best-effort cleanup temp files after the request
OPENAI_API_KEY is optional when local whisper command is configured, and required only for OpenAI fallback.
For codeharbor-whisper-transcribe, install runtime first: python3 -m pip install faster-whisper.
Replay Benchmark
Replay recorded prompts directly against codex CLI to quantify drift and latency:
npm run replay:cli-compat -- --input data/cli-compat-record.jsonl --out data/replay-report.json --max 50Useful flags:
--model <name>--workdir <path>--timeout-ms <n>--sandbox <mode>--approval <policy>--dangerous
Progress + Output
MATRIX_PROGRESS_UPDATES=true- emit stage progress updates (for example reasoning/thinking snippets)
MATRIX_PROGRESS_MIN_INTERVAL_MS=2500- minimum interval between progress updates
MATRIX_TYPING_TIMEOUT_MS=10000- typing indicator timeout; CodeHarbor refreshes typing state while handling a request
- Group rooms use notice edit (
m.replace) to coalesce progress and reduce spam. - Reply chunking is paragraph/code-block aware to avoid cutting fenced blocks when possible.
Tests
npm run typecheck
npm test
npm run build
npm run test:legacyIf Python legacy dependencies are missing, install them first:
python3 -m pip install -r requirements.txtLegacy Runtime
- Legacy Python runtime exists in
app/andtests/. - It is not part of default release/CI gates.
- Use
npm run test:legacyfor optional regression checks.
