neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
AI Agent Bridge는 AI 에이전트 CLI를 감독된 PTY 세션으로 실행하고 로컬 또는 원격 gRPC API를 통해 제공한다
AI Agent Bridge runs AI agent CLIs as supervised PTY sessions and exposes them through a local or remote gRPC API. A client can start a session, attach as a writer or observer, replay buffered output,
개발 도구Go · ShellGitHub @orchael온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
An API to startup ai agents on a remote machine
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A standalone gRPC daemon and SDK that manages AI agent subprocess lifecycles and exposes a PTY transport so any client can attach to, interact with, and replay the terminal output of a running AI agent — regardless of when it connected.
The bridge daemon spawns AI agents inside PTYs, buffers their output in a bounded ring buffer with sequence numbers, and serves a gRPC API. Clients can attach at any time, replay buffered output, and stream live PTY bytes. Authentication uses mTLS + JWT (Ed25519).
See the Docusaurus documentation under docs/ for architecture details.README 전문 보기 ↗A standalone gRPC daemon and SDK that manages AI agent subprocess lifecycles and exposes a PTY transport so any client can attach to, interact with, and replay the terminal output of a running AI agent — regardless of when it connected.
Supported providers: Claude, Codex, OpenCode, Gemini
Claude and Codex expose structured working, waiting, and idle status through
bridgectl session list and the public Go/gRPC API. This works without a hosted
service or telemetry. See session status
for setup, provider signals, compatibility mode, and troubleshooting.
Your App (Go)
↕ Go SDK or raw gRPC
bridgectl daemon
↕ PTY
AI Agent process (claude / codex / opencode / gemini)
The bridge daemon spawns AI agents inside PTYs, buffers their output in a bounded ring buffer with sequence numbers, and serves a gRPC API. Clients can attach at any time, replay buffered output, and stream live PTY bytes. Authentication uses mTLS + JWT (Ed25519).
See the Docusaurus documentation under docs/ for architecture details.
.nvmrc). Install via any of these methods:
nvm install after cloningbrew install node@24winget install OpenJS.NodeJS --version 24choco install nodejs --version=24curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - && sudo apt-get install -y nodejsprotoc + protoc-gen-go + protoc-gen-go-grpc for make build and regenerating .proto files (installed by make deps)Run scripts/setup-node.sh to verify your Node.js installation meets the project requirements, or scripts/setup-node.sh --install to attempt automatic installation via Homebrew on macOS.
git clone https://github.com/orchael/bridgectl.git
cd bridgectl
# Set up Node.js (pick one):
nvm install # if using nvm (recommended)
# Or verify your system Node: scripts/setup-node.sh
# Verify Node.js, install Go development tools, and download Go dependencies:
make deps
# Persist the Go tools directory on PATH for Bash or Zsh:
make setup
# Run the export printed by make setup to update this terminal too.
Set up env-secrets once for this repo. env-secrets in this environment uses AWS Secrets Manager:
cp .env.example .env
$EDITOR .env
cat > /tmp/bridgectl.secrets.env <<'EOF'
CLAUDE_CODE_OAUTH_TOKEN=
OPENAI_API_KEY=
EOF
$EDITOR /tmp/bridgectl.secrets.env
env-secrets aws secret upsert \
--name bridgectl/dev \
--file /tmp/bridgectl.secrets.env \
--profile <aws-profile> \
--region <aws-region>
The repo-local .env.example contains only the non-secret ENV_SECRETS_* settings. After you copy it to .env, the main make targets will call env-secrets aws automatically and load the secret named by ENV_SECRETS_AWS_SECRET.
nvm install
corepack enable
corepack prepare [email protected] --activate
pnpm install --frozen-lockfile
make build-cli
bin/bridgectl server start
This starts the bridge in local Unix socket mode. Use bin/bridgectl server start --listen <tailscale-ip>:9445 when you want secure remote access over Tailscale.
make chat-claude # or chat-opencode, chat-codex, chat-gemini
A development machine can keep its packaged/system bridgectl service running while you build and test this repository. The development targets use ./bin/bridgectl explicitly and set BRIDGECTL_STATE_DIR=$(pwd)/.dev/bridgectl, which isolates the dev server's socket, PID, certificates, and discovery files from ~/.config/bridgectl. The dev config intentionally does not bind TCP port 9445.
# Build the repository binary. This never replaces /usr/bin/bridgectl.
make build
# Compare the isolated development server with the installed server.
make dev-server-status
# Start/stop/restart the repository-local server in the background.
make dev-server-start
make dev-server-stop
make dev-server-restart # stop, wait for it to actually exit, then start — use after rebuilding a fix
make dev-server-logs
# Log into Bridge against the isolated dev state dir (never the installed
# service's real config/credentials — see "Debugging high CPU usage" below).
make dev-login
# Run an agent through the repository-local bridgectl build.
make dev-session-codex DEV_REPO=/workspace/my-repo
make dev-session-claude DEV_REPO=/workspace/my-repo
# Run the provider CLI with the local Bridge diagnostic MCP server.
make dev-codex DEV_REPO=/workspace/my-repo
make dev-claude DEV_REPO=/workspace/my-repo
# Standalone escape hatch when Bridge/Docker is unavailable: same bridgectl
# wrapper and MCP script, just skipping the local Bridge MCP connect/check.
make dev-codex DEV_MCP=0
make dev-claude DEV_MCP=0
# Fully bridgectl-independent: no build, no dev server, no Docker, no MCP
# script at all — just the real agent CLI, loading credentials from
# AGENTS_ENV_FILE (~/.config/bridgectl/agents.env by default). Use this when
# bridgectl itself (its build, its dev server, or its enrollment) is broken
# and you just want an agent running against this repo.
make local-codex
make local-claude
dev-codex and dev-claude attach a bridge_dev MCP server for that launch.
It exposes list_sessions, list_attention, inspect_session, and diagnose_session through
the local Bridge API, including authoritative diagnostics requested over the
bridgectl control connection. The adapter uses stdio, starts and stops with the
agent, and opens no listening port. It does not change global Codex or Claude
configuration, auto-approve tools, or expose response/approval tools.
Call list_sessions with {} to discover all registry sessions, or with
{"status":"running"} to filter by lifecycle status: starting, running,
attached, stopping, stopped, failed, or unknown. Results are grouped by installation
and include offline/stale last-known sessions; installations with no matching
sessions are omitted. list_attention retains its live attention-only behavior.
The local Bridge API must include the list_sessions tool; rebuild that API
and restart the dev agent after updating both repositories.
Prerequisites are Node.js (the repository specifies Node 24+), Docker access,
the running local Bridge dev stack (bridge-web-1, bridge-api-1,
bridge-control-1, bridge-db-1), and a dev enrollment:
make dev-login LOGIN_ARGS="--bridge https://bridge.orchael.dev --no-browser"
make dev-server-restart DEV_CONFIG="$PWD/.dev/bridgectl/bridge.yaml"
make dev-mcp-check
make dev-codex # or make dev-claude
The adapter reads DEV_STATE_DIR/bridge-enrollment.json, looks up that
installation's enrollment approver in the local database, and sends requests as
that user and organization. Bridge still checks current membership. Signing uses
the web container's service secret; no secret is copied into agent configuration.
This adapter is for the local bridge.orchael.dev Docker stack, not production
or remote MCP onboarding. The web container must have BRIDGE_PUBLIC_URL set to
https://bridge.orchael.dev and BRIDGE_API_URL pointing to the local API service.
To check a particular session through MCP:
make dev-mcp-check DEV_MCP_SESSION=<session-id>
make test-dev-mcp
The check verifies MCP initialization and tool discovery; with a session ID it
also prints the diagnostic tool result. An unavailable authoritative source is
reported in the diagnostic itself, so inspect sources.bridgectl_authoritative.
If containers have different names, set DEV_MCP_WEB_CONTAINER,
DEV_MCP_DB_CONTAINER, and, if needed, DEV_MCP_DATABASE on the make command.
DEV_STATE_DIR selects a different isolated enrollment. These settings also
apply when DEV_REPO points to another working directory. Missing enrollment,
membership, or Docker access fails the MCP preflight before starting the agent;
use DEV_MCP=0 to run without it. dev-session-codex and dev-session-claude
continue to use the daemon's provider configuration and are unaffected.
For another stdio client, make --no-print-directory dev-mcp runs the adapter
directly. Its stdout is reserved for MCP JSON-RPC messages; status and errors go
to stderr. Codex's per-launch settings use the documented
MCP configuration.
dev-server-start always enables an opt-in pprof debug endpoint on 127.0.0.1:6061 (loopback only; disabled by default and never started on the installed/packaged service unless its environment also sets BRIDGECTL_PPROF_ADDR — see cmd/bridgectl/server.go). While the dev server is reproducing high CPU (e.g. with dev-session-codex/dev-session-claude attached and active), sample it:
# Capture a 30s CPU profile and open it interactively (top, list, web, etc.)
make dev-server-cpu-profile
# Dump all goroutine stacks (useful for spotting a stuck/busy-looping goroutine)
make dev-server-goroutines
To profile the installed/packaged service instead, set BRIDGECTL_PPROF_ADDR=127.0.0.1:6061 in its environment (e.g. EnvironmentFile/Environment= in the systemd unit) and restart it — without that variable set, the server never starts a listener for it, so the endpoint is unreachable by default.
If reproducing the issue needs real control/telemetry wiring (only active once managed_by_bridge config is present — otherwise the dev server is a much simpler, lower-CPU code path), run make dev-login rather than bin/bridgectl login directly: it seeds the isolated config file first so login can never fall through to and rewrite the installed service's real config. After changing code to test a fix, use make dev-server-restart (not a separate stop + start) to avoid a race where the old process still holds the pprof port when the new one tries to bind it.
See docs/docs/guides/debugging-high-cpu.md for the full methodology (confirming threads vs. processes, strace, reading a goroutine dump, and narrowing a hot function to an algorithmic cause) and a worked example.
Do not use plain bridgectl when validating local changes: that resolves the installed binary on PATH. The dev-* targets deliberately use $(pwd)/bin/bridgectl.
make up
Mounts ~/repos → /repos and ./certs → /app/certs. The prebuilt image is available at ghcr.io/orchael/bridgectl.
Use make down to stop the stack while preserving its volumes. The destructive
make reset and make reset-local targets ask for confirmation before running
Compose down -v and deleting their stack's volumes.
Interaction telemetry is disabled by default. Enable the
telemetry block in your bridge YAML, start the server with that config, then
inspect the rolling report or provider-neutral export:
telemetry:
enabled: true
# Empty generates and persists a stable UUID in the bridge state directory.
source_id: ""
# Empty retains segments locally; host:port enables gRPC delivery.
collector_target: "127.0.0.1:9464"
# Plaintext is only appropriate for loopback or a private Compose network.
collector_insecure: true
collector_ca: ""
collector_server_name: ""
spool_dir: "~/.config/bridgectl/telemetry/segments"
kinds: ["question", "answer"]
queue_size: 1024
rolling_window: "168h"
flush_interval: "30s"
retry_interval: "1s"
max_segment_bytes: 10485760
max_disk_space: 1GB
include_redacted_text: false
bin/bridgectl server start --config config/bridge.yaml
bin/bridgectl telemetry report --events ~/.config/bridgectl/telemetry/segments --since 24h
bin/bridgectl telemetry export --events ~/.config/bridgectl/telemetry/segments --since 24h --format json
Select retained data with kinds; for example, kinds: [question, answer]
excludes lifecycle records. include_redacted_text: false is the
privacy-preserving default. Local files are mode 0600, rotate at
max_segment_bytes, and evict the oldest immutable segments when total active
and immutable storage exceeds max_disk_space. Reports and exports read the
retained immutable segments in order.
Both the bridge-local outbox and collector-local volume default to a 1 GB
(1,000,000,000-byte) disk budget. Decimal units such as GB and binary units
such as GiB are accepted. Override the collector with
TELEMETRY_MAX_SEGMENT_BYTES and TELEMETRY_MAX_DISK_SPACE; configure the
bridge outbox independently with max_segment_bytes and max_disk_space in
bridge.yaml.
The supplied Compose service health check matches its plaintext private-network
configuration. The collector image itself does not hard-code a transport mode;
TLS deployments should configure a runtime health check with telemetry health
and the appropriate --ca and --server-name options.
Every new event uses schema v2 and includes a source_id; telemetry session
identity is the composite (source_id, session_id). Set source_id explicitly when an
orchestrator owns bridge identity, or leave it empty to generate a UUID once at
the bridge state directory (by default
~/.config/bridgectl/telemetry/source-id). The generated file is mode 0600.
Legacy events without source_id remain readable in an empty legacy namespace.
Schema v2 emits one session_context event after session_started. It includes
OS/architecture, branch and commit, and stable HMAC IDs for
the machine, working directory, and repository. It never stores the raw working
directory, repository path, remote URL credentials, hostname, Git author, or
environment variables. The private 32-byte HMAC key defaults to
~/.config/bridgectl/telemetry/identity-key (mode 0600) and can be relocated
with identity_key_file. Optional actor_id and source_label values are
operator-provided non-secret labels; they are not authenticated identities.
Supported concrete kinds are session_started, session_context, provider_output,
user_input, question, answer, and session_ended. To build a complete
redacted interaction corpus, explicitly enable all kinds and retained text:
telemetry:
enabled: true
kinds: [all]
include_redacted_text: true
all must be the only list entry and expands to every concrete kind. The safe
default does not include provider_output or user_input, so upgrading does
not silently start recording transcripts. provider_output records normal and
thinking streams; user_input records only data submitted by the authorized
active writer. Every event has a per-session sequence number, and stream events
include their direction, stream type, and original byte count. Terminal
controls and recognized secrets are removed before local spooling or gRPC
delivery. Invalid UTF-8 content is omitted with its byte count, digest, and
omission reason instead of being forwarded as opaque data. Chunk boundaries are
reassembled before redaction, so split UTF-8 characters and split secret tokens
cannot bypass those checks.
An individual interaction record is buffered up to 1 MiB for this purpose. A
larger record with no boundary is retained as byte count, digest, and the
buffer_limit omission reason rather than unbounded raw text.
For a small, non-production analysis reference covering deterministic findings,
read-only HTTP APIs, and bounded LLM evidence packets, see
examples/telemetry-analysis.
Full capture can contain personal or proprietary material even after
best-effort redaction. Protect collector volumes and S3 access, use an explicit
retention policy, and enable it only for users and projects that have agreed to
the collection. Removing provider_output and user_input from kinds
immediately returns collection to derived telemetry only.
To run the collector with its own persistent Docker volume:
make up-collector
make ps-collector
make logs-collector
Point the bridge at 127.0.0.1:9464 using collector_target. The standalone
Compose file publishes only to host loopback. Plaintext gRPC requires the
explicit collector_insecure: true setting and must not be exposed to a public
network. For a remote collector, start it with --tls-cert and --tls-key,
then configure collector_ca and optionally collector_server_name on the
bridge. These flags authenticate the collector to the bridge; they do not
authenticate bridge clients. Keep the collector behind an operator-managed
network ACL or authenticated proxy. Native tenant/actor authentication is
tracked in orchael/bridge#5.
Completeness checkpoints are enabled by default and record capture scope, event counts, and delivery losses. HTTPS batches respect Bridge's 1 MiB/1,000-record limits, with lossless fragments for oversized events and matching S3 receipts required before deleting local data. See the telemetry guide for audit and recovery details.
The bridge always writes redacted events to its bounded local spool before streaming them. The collector acknowledges a segment only after it is durably synced to the collector-owned volume. If the collector is unavailable, the bridge retries unacknowledged segments without blocking agent sessions; if the configured spool fills, it evicts the oldest segment and logs a warning.
Inspect the collector-owned volume without copying it to the host:
docker compose -f telemetry/docker-compose.yml exec telemetry-collector \
bridgectl telemetry report --events /data/segments
To upload the same bounded segments to an existing S3 bucket, supply standard AWS credentials (or use the container/instance role) and set:
export AWS_REGION=us-east-1
export TELEMETRY_S3_BUCKET=your-existing-bucket
export TELEMETRY_S3_PREFIX=bridgectl/telemetry
make up-collector
Stop the collector with make down-collector. This removes the container and
network but preserves the collector's named telemetry volume. Use
make reset-collector only when you also want to delete that volume; it asks
for confirmation first. The Step CA stack has the same destructive behavior in
make reset-step-ca.
The collector keeps each segment on its volume until PutObject succeeds. S3
failures therefore retry from the collector spool. Object keys are partitioned
by UTC date and use the stable segment ID, making retries idempotent.
Set the collector's --max-segment-bytes to at least the bridge's
max_segment_bytes; otherwise the collector rejects oversized segments and the
bridge retains them for retry until its bounded spool evicts them.
To exercise the full bridge → collector → volume path without provider credentials, run:
make test-e2e-live-telemetry
The test logs the filtered permission-question and accepted-answer events that it reads from the collector-owned Compose volume.
After creating a test bucket, exercise the real gRPC → S3 path with credentials that can list, put, get, and delete objects under the configured test prefix:
BRIDGECTL_TELEMETRY_S3_BUCKET=your-existing-test-bucket \
make test-telemetry-s3-e2e
The test never creates or deletes the bucket. It uses a unique prefix and deletes only the objects it created there.
make smoke
This validates the repo Dockerfile and Compose stack by starting the bridge in Docker and running an authenticated gRPC health check. It also verifies config-driven provider fallback by requesting a deliberately unavailable smoke provider and asserting the configured fallback provider is selected.
Supported on Apple Silicon and Intel Macs.
brew install orchael/bridgectl/bridgectl
The released binaries are signed with a Developer ID certificate and notarized by Apple, so they run without a Gatekeeper prompt.
Homebrew does not install the provider CLIs or their runtime. bridgectl launches claude, codex, gemini, and opencode through Node.js 24, so install that separately:
brew install node@24
Run the daemon at login (optional):
bridgectl server install-agent --start
This writes a launchd user agent to ~/Library/LaunchAgents/com.orchael.bridgectl.plist. The agent points at the absolute path of the bridgectl binary you ran the command with, so it is correct for Homebrew on Apple Silicon (/opt/homebrew), Homebrew on Intel (/usr/local), and manual installs alike.
It also copies the PATH of the shell you run it from into the plist. That matters: launchd gives agents a minimal PATH, and the server resolves node and the provider CLIs through PATH — without this the daemon starts but every session fails with node not found on PATH. Run the command from a shell where node --version works.
Logs go to ~/Library/Logs/bridgectl/bridgectl.log and bridgectl.err. Omit --start to write the plist without loading it.
To remove the agent:
bridgectl server uninstall-agent
brew uninstall bridgectl unloads the agent too, and brew uninstall --zap bridgectl additionally deletes the plist and the logs.
Unsigned local builds remain available via make build-cli — see Build and install.
Windows binaries are published starting with this release (see Windows Install below), but the interactive session/PTY path is not yet functional there — see that section for what works today and #259 for the rest.
Supported releases: Ubuntu 24.04 (noble) and 25.04 (plucky) on amd64.
Quick install:
curl -fsSL https://orchael.github.io/bridgectl/install.sh | sudo bash
systemctl --user enable --now bridge.service
Manual apt setup (noble example):
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://orchael.github.io/bridgectl/apt/bridgectl-archive-keyring.asc \
| sudo gpg --dearmor -o /etc/apt/keyrings/bridgectl.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bridgectl.gpg] \
https://orchael.github.io/bridgectl/apt noble main" \
| sudo tee /etc/apt/sources.list.d/bridgectl.list >/dev/null
sudo apt-get update
sudo apt-get install -y bridgectl
systemctl --user enable --now bridge.service
Run the systemctl --user command as the login user who will run the bridge.
The package installs bridge.service, not a system unit named
bridgectl.service. A managed host may provide a custom user unit instead;
enable only one bridge unit per user.
Supported Ubuntu suites: noble (24.04 LTS) and plucky (25.04). Replace noble above with plucky if you are on Ubuntu 25.04. The repository does not publish a stable or jammy suite — using any other suite name will result in a "does not have a Release file" error from apt.
Ansible: use ansible_distribution_release for the suite and dpkg --print-architecture for the arch. Do not use ansible_architecture — it returns the kernel arch (x86_64) rather than the Debian package arch (amd64), which causes apt to look for a non-existent binary-x86_64/Packages path.
- name: Get Debian architecture
ansible.builtin.command: dpkg --print-architecture
register: dpkg_arch
changed_when: false
- name: Ensure /etc/apt/keyrings exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Download bridgectl signing key
ansible.builtin.get_url:
url: https://orchael.github.io/bridgectl/apt/bridgectl-archive-keyring.asc
dest: /tmp/bridgectl-keyring.asc
mode: '0644'
- name: Dearmor signing key
ansible.builtin.command: >
gpg --dearmor -o /etc/apt/keyrings/bridgectl.gpg /tmp/bridgectl-keyring.asc
args:
creates: /etc/apt/keyrings/bridgectl.gpg
- name: Add bridgectl apt repository
ansible.builtin.apt_repository:
repo: "deb [arch={{ dpkg_arch.stdout }} signed-by=/etc/apt/keyrings/bridgectl.gpg] https://orchael.github.io/bridgectl/apt {{ ansible_distribution_release }} main"
state: present
filename: bridgectl
notify: Update apt cache
The packaged service installs a minimal config at /etc/bridgectl/bridge.yaml and listens on 127.0.0.1:9445 by default. It does not bundle provider CLIs or API keys. For production use you must install the provider CLIs separately, add provider configuration, and decide how the service account should access the target repositories.
Download bridgectl_<version>_windows_amd64.zip from GitHub Releases.
Extract it and put bridgectl.exe on your PATH.
Install Node.js 24, which the provider CLIs run through:
winget install OpenJS.NodeJS --version 24
Verify the install:
bridgectl.exe --version
What works today: the Windows build cross-compiles and ships a release artifact. CI runs bridgectl.exe --version and bridgectl.exe doctor natively on a Windows runner on every change, so the client-only and diagnostic commands are verified to actually run, not just compile.
What does not work yet: the interactive session path (bridgectl.exe session start, bridgectl.exe run) depends on a pseudo-terminal, and this build has no real PTY support on Windows (the underlying creack/pty dependency compiles but returns an "unsupported" error for every call on this platform) — it fails with a clear error rather than hanging or behaving unpredictably. Real interactive support needs ConPTY integration, tracked in #259.
Until interactive sessions work natively, use one of:
docker run ... ghcr.io/orchael/bridgectl:latest (see Docker above).Add the SDK to your Go module:
go get github.com/orchael/bridgectl/pkg/bridgeclient
import (
"context"
"github.com/orchael/bridgectl/pkg/bridgeclient"
bridgev1 "github.com/orchael/bridgectl/gen/bridge/v1"
)
// Connect (no TLS — for local dev with auth disabled)
client, err := bridgeclient.New(
bridgeclient.WithTarget("127.0.0.1:9445"),
)
if err != nil { ... }
defer client.Close()
ctx := context.Background()
// Start an agent session
_, err = client.StartSession(ctx, &bridgev1.StartSessionRequest{
ProjectId: "my-project",
SessionId: "session-001",
RepoPath: "/path/to/repo",
Provider: "claude",
})
// Attach and stream output
stream, err := client.AttachSession(ctx, &bridgev1.AttachSessionRequest{
SessionId: "session-001",
})
stream.RecvAll(ctx, func(ev *bridgev1.AttachSessionEvent) error {
fmt.Print(string(ev.Payload))
return nil
})
// Send input
client.WriteInput(ctx, &bridgev1.WriteInputRequest{
SessionId: "session-001",
Data: []byte("hello\n"),
})
client, err := bridgeclient.New(
bridgeclient.WithTarget("bridge.internal:9445"),
bridgeclient.WithMTLS(bridgeclient.MTLSConfig{
CABundlePath: "certs/ca-bundle.crt",
CertPath: "certs/client.crt",
KeyPath: "certs/client.key",
ServerName: "bridge.local",
}),
bridgeclient.WithJWT(bridgeclient.JWTConfig{
PrivateKeyPath: "certs/jwt-signing.key",
Issuer: "my-service",
Audience: "bridge",
}),
)
Full Go SDK reference: docs/docs/reference/go-sdk.md
bridgectl supports mTLS for workload identity and JWTs for authorization. Bring your existing PKI or use the Step CA integration for automated enrollment, short-lived certificates, and renewal.
| Mode | Transport | Identity | Use case |
|---|---|---|---|
local | Plaintext (loopback) | None | Development |
tls | Server TLS | JWT | Trusted networks |
mtls | Mutual TLS | Client cert + JWT | Production |
Supported certificate sources: Step CA, Kubernetes cert-manager, SPIFFE/SPIRE, Vault PKI, AWS Private CA, enterprise CAs, and manually managed X.509 certificates.
Production deployments should use mTLS unless the environment provides an equivalent trusted transport boundary.
Secure by default. Bring your own identity.
See Security Overview for details.
Install grpcurl to call the bridge from a shell.
With mTLS (after starting a secure TCP server):
grpcurl \
-cacert certs/ca-bundle.crt \
-cert certs/dev-client.crt \
-key certs/dev-client.key \
-servername bridge.local \
-import-path proto -proto bridge/v1/bridge.proto \
127.0.0.1:9445 bridge.v1.BridgeService/Health
Without TLS (auth disabled):
# Health check
grpcurl -plaintext -import-path proto -proto bridge/v1/bridge.proto \
127.0.0.1:9445 bridge.v1.BridgeService/Health
# Start a session
grpcurl -plaintext -import-path proto -proto bridge/v1/bridge.proto \
-d '{"project_id":"dev","session_id":"s1","repo_path":"/tmp","provider":"claude"}' \
127.0.0.1:9445 bridge.v1.BridgeService/StartSession
# Send input
grpcurl -plaintext -import-path proto -proto bridge/v1/bridge.proto \
-d '{"session_id":"s1","client_id":"c1","data":"aGVsbG8K"}' \
127.0.0.1:9445 bridge.v1.BridgeService/WriteInput
# Stream output
grpcurl -plaintext -import-path proto -proto bridge/v1/bridge.proto \
-d '{"session_id":"s1","client_id":"c1"}' \
127.0.0.1:9445 bridge.v1.BridgeService/AttachSession
# Stop a session
grpcurl -plaintext -import-path proto -proto bridge/v1/bridge.proto \
-d '{"session_id":"s1"}' \
127.0.0.1:9445 bridge.v1.BridgeService/StopSession
Note: data is base64-encoded bytes. grpcurl does not support JWT injection — use the Go SDK for JWT-authenticated calls.
Full API reference: docs/docs/reference/grpc-api.md
Full documentation source: docs/docs/intro.md
| Provider | Binary | Required auth |
|---|---|---|
claude | ./node_modules/.bin/claude | CLAUDE_CODE_OAUTH_TOKEN |
opencode | ./node_modules/.bin/opencode | OPENAI_API_KEY |
codex | ./node_modules/.bin/codex | Existing account, CODEX_AUTH, or API key (precedence) |
gemini | agy | OAuth2 credentials (~/.gemini/oauth_creds.json) |
Providers are configured in config/bridge-dev.yaml. See docs/docs/reference/configuration.md for configuration reference.
make deps first runs setup-node to verify the Node.js version in .nvmrc
(it reports installation instructions if Node is missing or incompatible).
It requires Go and runs tools to install the protobuf Go plugins, plus goimports and
golangci-lint with pinned versions. It installs missing protoc through Homebrew (with confirmation prompts disabled)
or apt (using sudo when needed); on other systems, install protoc first with your
package manager. Set PROTOC_INCLUDE when protobuf headers are outside the
Homebrew prefix or /usr/include. Installing Node.js, provider CLIs, Docker, and Git hooks
are separate setup steps. Use make web-install or make docs-install for the
corresponding frontend dependencies.
make setup adds go env GOBIN (or the first GOPATH entry's bin directory)
to ~/.bashrc and the active Bash login profile, or ${ZDOTDIR:-$HOME}/.zshrc,
for future interactive shells. Bash uses the first existing file among
~/.bash_profile, ~/.bash_login, and ~/.profile; setup creates .bash_profile
if none exists. Repeated runs and sourcing do not duplicate the new PATH entry. Set SETUP_SHELL_RC to use a custom
Bash/Zsh startup file. Make cannot change the calling shell's environment, so
run the printed export command to update your current terminal immediately.
make setup also installs pre-commit (via Homebrew or apt-get if it is not
already on PATH) and runs scripts/setup-git-hooks.sh, which registers the
pre-commit and pre-push git hooks from .pre-commit-config.yaml. Run
make setup at the start of every session (including agent sessions) so
gofmt/goimports/golangci-lint and go test -race actually run locally
before a commit or push, instead of only in CI.
| Target | Description |
|---|---|
make setup | Persist the Go tools directory on PATH for Bash/Zsh; install pre-commit and register the git hooks |
make deps | Verify Node.js and install Go development tools, protoc, and Go module dependencies |
make build | Build bin/bridgectl and bin/bridge-ca |
make test | Run unit tests with race detection |
make test-e2e | Run the Dockerized end-to-end test suite |
make test-e2e-unprotected | Manually run live Docker SDK e2e tests for Codex/Claude protected and unprotected modes |
make test-cover | Run tests with coverage report |
make smoke | Run Docker-based smoke validation |
make smoke-apt-local | Build a .deb, create a local apt repo, and verify install in Ubuntu containers |
make smoke-ec2 | Provision an EC2 host, install from the hosted apt repo, and run a health check |
make chat-claude | Interactive PTY session with Claude |
make chat-opencode | Interactive PTY session with OpenCode |
make chat-codex | Interactive PTY session with Codex |
make chat-gemini | Interactive PTY session with Gemini |
make proto | Regenerate protobuf Go code (after editing .proto) |
make lint | Run golangci-lint |
make fmt | Format code with gofmt + goimports |
make dev-setup | Build binaries and generate dev certificates |
make docs-build | Build the Docusaurus documentation site |
make setup-node | Check Node.js installation and print setup guidance |
make certs | Initialize a bridge CA in |
Deprecated:
bridge-cais deprecated. For new deployments, usebridgectl server startwhich auto-generates PKI, or use the Step CA integration for automated certificate lifecycle management. Thecross-signandverifysubcommands remain available during the deprecation period.bridge-cawill be removed in the next major release. See #154 for details.
bridge-ca init # Initialize a new ECDSA P-384 CA
bridge-ca issue # Issue a server or client certificate
bridge-ca cross-sign # Cross-sign an external CA for multi-tenant trust
bridge-ca bundle # Build a trust bundle from multiple CA certs
bridge-ca jwt-keygen # Generate an Ed25519 keypair for JWT signing
bridge-ca verify # Verify a certificate against a trust bundle
Run bridge-ca <command> --help for flags.
cmd/bridgectl/ User-session server and CLI
cmd/bridge-ca/ Certificate management CLI
pkg/bridgeclient/ Go SDK
proto/bridge/v1/ Protobuf service definitions
gen/bridge/v1/ Generated protobuf Go code (do not edit)
internal/auth/ mTLS, JWT, gRPC interceptors
internal/bridge/ Session supervisor, event buffer, registry, policy
internal/config/ YAML configuration loader
internal/pki/ CA management, cert issuance, cross-signing
internal/provider/ Stdio/PTY provider adapters
internal/server/ gRPC server implementation + rate limiting
examples/chat/ Interactive PTY passthrough example
docs/ Docusaurus documentation site
config/ Default configuration files
scripts/ Development helper scripts
Releases are triggered via Actions → Publish → Run workflow with a patch, minor, or major input. The workflow bumps the version, creates a Git tag, builds the container image, builds and signs .deb packages, and publishes the apt repository to GitHub Pages.
| Secret | Description |
|---|---|
APT_REPO_GPG_PRIVATE_KEY_B64 | Base64-encoded GPG private key used to sign the apt repository |
APT_REPO_GPG_PASSPHRASE | Passphrase for the GPG key (omit if the key has no passphrase) |
AWS_SMOKE_ROLE_ARN | (optional) IAM role ARN for the EC2 smoke test; skipped if not set |
SMOKE_AWS_REGION | (optional) AWS region for the EC2 smoke test |
# Generate a dedicated signing key (no passphrase)
gpg --batch --gen-key <<'EOF'
Key-Type: RSA
Key-Length: 4096
Name-Real: AI Agent Bridge APT Signing
Name-Email: [email protected]
Expire-Date: 0
%no-protection
%commit
EOF
# Export and base64-encode the private key
gpg --export-secret-keys --armor [email protected] | base64 -w 0
Add the base64 output as APT_REPO_GPG_PRIVATE_KEY_B64 in Settings → Secrets and variables → Actions. The public key is exported automatically into the apt repository at publish time as bridgectl-archive-keyring.asc.
MIT License - see LICENSE file for details.
certs/make clean | Remove build artifacts |