{
  "markdown": "# brokre — AI-safe Credential Broker\n\n<!-- README-I18N:START -->\n\n**English** | [简体中文](README.zh-CN.md)\n\n<!-- README-I18N:END -->\n\n`brokre` is a **local credential broker** for AI agents and humans. Use it with Cursor, Claude Code, Kimi Code, Trae, OpenClaw, Hermes Agent, ChatClaw, and other MCP-capable clients to run `ssh`, `mysql`, `psql`, and more — **passwords never enter AI context, environment variables, or `ps` output**. It wraps **any CLI on your `PATH`** — not only SSH or MySQL — and injects saved passwords at the prompt **without exposing plaintext** to the AI process, shell history, or process environment.\n\nDeveloped by [Techinone](https://www.tio.tech) (成都同创合一科技有限公司).\n\n## What's New in 0.2.17\n\n**0.2.17** is the current release: **SessionRelay routed SSH by default**, **multi-hop bastion routes**, **local-only list by default**, and the existing **one-command npm install / auto MCP registration / auto binary upgrade** flow.\n\n### npm — install, auto-update, auto MCP setup\n\n```bash\nnpm install -g brokre          # or: npx -y brokre@latest\n```\n\n| Capability | What happens |\n|------------|----------------|\n| **Auto MCP registration** | `postinstall` runs `brokre-setup-mcp` — detects **installed** IDEs only and merges `npx -y brokre@latest` into each global MCP config. Re-run: `brokre mcp setup` or `npx brokre-setup-mcp`. Skip: `BROKRE_MCP_SKIP_SETUP=1`. |\n| **Auto binary upgrade** | On each MCP start, compares npm package version with `PATH` / `~/.brokre/bin/brokre`; downloads matching [GitHub Release](https://github.com/Furowu/brokre/releases) when missing or older. |\n| **CLI without npm** | `brokre version` / `brokre upgrade` for install.sh users; `brokre mcp setup` to register MCP after installing IDEs. |\n| **Supported IDEs** | Cursor, VS Code, VS Code Insiders, Claude Code, Claude Desktop, Trae, Kimi Code, Windsurf, OpenClaw — see [packages/brokre-mcp/README.md](packages/brokre-mcp/README.md). |\n\nRecommended MCP config (also applied by auto-setup):\n\n```json\n{ \"command\": \"npx\", \"args\": [\"-y\", \"brokre@latest\"] }\n```\n\n### Bastion broker — cluster management\n\nThe bastion layer lets AI agents operate **many hosts behind one jump box** without copying vault passwords into context or scattering secrets on the laptop.\n\n| Advantage | What it means in practice |\n|-----------|----------------------------|\n| **Single control plane** | Register a bastion SSH alias (`b150`), sync inner aliases from remote brokre, and drive the whole cluster from `brokre list` / MCP `brokre_list` |\n| **Smart routing** | `b150::db`, `b150::app-01`, multi-hop `b1::b2::inner` — route separator `::`; routed SSH uses SessionRelay by default |\n| **Secrets stay on the bastion** | Routed exec runs `~/.brokre/bin/brokre` on the jump host; laptop holds metadata and session gate, not inner-host passwords |\n| **Human gate, agent-friendly** | Bastion outbound requires unlock (TTY, `/bastion-auth`, or MCP URL elicitation); gate auth survives manage UI idle expiry so long MCP runs keep working |\n| **Cluster-safe defaults** | Local-only list by default; explicit bastion discovery; reachability probes with ms timeouts and concurrency caps; loop detection and audit `route`/`bastion` fields |\n| **Privileged ops over routes** | `brokre_exec_elevated` and `sudo`/`sudo -i` paths work through bastions with session reuse and PTY hardening |\n\nTypical flow for a K8s / DB / batch cluster behind one entry host:\n\n```bash\nbrokre bastion enable b150\nbrokre bastion sync b150 --json          # pull inner alias catalog\nbrokre bastion unlock\nbrokre list --include-bastions --json    # b150::db, b150::worker-01, …\nbrokre ssh b150::db systemctl status   # MCP: brokre_exec with routed alias\n```\n\nMCP equivalent:\n\n```json\n{ \"binary\": \"ssh\", \"args\": [\"b150::db\", \"uname\", \"-a\"] }\n```\n\n**Gate policy (default vs strict)** — see [Bastion gate policy](#bastion-gate-policy-default-vs-strict) below. Gate is inactive until `brokre bastion set-key`; then **default** unlocks only bastion outbound paths; **strict** requires unlock for every exec while list remains local-only unless bastion discovery is requested.\n\nSee [Cross-network list inheritance](#cross-network-list-inheritance-bastion-broker) and [Bastion proxy](#bastion-proxy-cross-network--intranet-entry) below for setup details.\n\n## CLI security (core)\n\nbrokre is built around one rule: **secrets stay out of the AI's reach and out of observable process state.**\n\n| Layer | What brokre does |\n|-------|-----------------|\n| **No env / `ps` leakage** | Injection is PTY prompt-based — passwords are never passed via `-p`, `SSHPASS`, `MYSQL_PWD`, or exported env vars |\n| **Parent never holds plaintext** (Unix) | Saved passwords decrypt in a short-lived `brokre --internal-injector` child, written once to the PTY, then the child exits |\n| **AI cannot `reveal`** | `brokre reveal` requires a real TTY + master passphrase; unavailable in the web UI and **not exposed via MCP** |\n| **Vault at rest** | Per-field AES-256-GCM; DEK wrapped with OS keyring (Linux) or `~/.brokre/.master_kek` (macOS) + optional Argon2id reveal passphrase |\n| **Audit** | HMAC-chained JSONL at `~/.brokre/audit/audit.log`; `brokre audit list` queries history (metadata only); `brokre audit verify` detects tampering |\n| **MCP boundary** | MCP exposes metadata (`brokre_list`), exec (`brokre_exec`, `brokre_exec_elevated`), `brokre_setup`, and read-only audit (`brokre_audit_list`, `brokre_audit_verify`) — no passwords, session tokens, or `reveal` |\n| **Manage UI** | Binds `127.0.0.1` only; passwords are **write-only**; audit log tab for history; session token printed in your terminal, never returned to AI |\n| **OS hardening** | Core dumps disabled, ptrace checks (Linux), optional `mlockall` — see [docs/HARDENING.md](docs/HARDENING.md) |\n\nFull threat model: [SECURITY.md](SECURITY.md), [THREAT_MODEL.md](THREAT_MODEL.md).\n\n## Any CLI on `PATH` (generic by design)\n\nbrokre is **not** a fixed list of database/SSH wrappers. The core model is:\n\n```bash\nbrokre <any-cli-on-PATH> [args...]\n```\n\nFirst connection: run verbatim, capture the password you type at the prompt, offer to save as an alias.  \nNext time: `brokre <cli> <alias> …` auto-injects — AI and scripts only see the alias name.\n\n**Preset prompt patterns** ship for common tools (ssh, mysql, psql, redis-cli, ftp, clickhouse, git, docker, kubectl, sudo, …). **Everything else** uses a generic `password:` / `passphrase:` matcher — no code changes required.\n\n```bash\nbrokre gsql prod-cluster -c \"SELECT 1\"    # any proprietary CLI on PATH\nbrokre kubectl get pods                   # if your cluster CLI prompts for a password\nbrokre my-internal-tool --host db.internal\n```\n\nCustomize when needed:\n\n- `~/.brokre/prompts.toml` — per-binary prompt regex overrides\n- `~/.brokre/manage.toml` — custom sections in the manage UI (e.g. GaussDB, internal tools)\n\nBuilt-in manage UI tabs (when the binary is installed) include SSH, FTP, MySQL, PostgreSQL, Redis, ClickHouse, MinIO — convenience only; the **PTY wrapper works for any CLI**.\n\n## Install (MCP first — recommended for AI)\n\nThe npm package [`brokre`](https://www.npmjs.com/package/brokre) launches the local `brokre mcp` server over stdio for Cursor, Claude Code, Kimi Code, Trae, OpenClaw, Windsurf, VS Code, and other MCP clients.\n\n### Choose your install path\n\n| Path | Best for | Install | MCP in IDEs | CLI upgrade |\n|------|----------|---------|-------------|-------------|\n| **npm** (recommended) | AI users; want one command | `npm install -g brokre` | **Automatic** on install (`postinstall`) | npm + auto-download on each MCP start |\n| **install.sh / Homebrew** | Production; no Node for daily use | `curl … \\| bash` or `brew install brokre` | Run `brokre mcp setup` after IDE install | `brokre version` / `brokre upgrade` |\n| **Manual MCP JSON** | Custom layouts only | CLI or npm already present | Edit IDE config by hand | Depends on how CLI was installed |\n\n**Recommended MCP entry** (also what auto-setup writes):\n\n```json\n{ \"command\": \"npx\", \"args\": [\"-y\", \"brokre@latest\"] }\n```\n\nNo Node — point MCP at the native binary: `{ \"command\": \"brokre\", \"args\": [\"mcp\"] }`.\n\n### Path A — npm one-liner (0.2.8+)\n\n```bash\nnpm install -g brokre\n# or without global install:\nnpx -y brokre@latest\n```\n\nOn `npm install`, three things happen automatically:\n\n1. **MCP launcher** — `brokre-mcp` / `npx -y brokre@latest` spawns `brokre mcp`.\n2. **IDE auto-registration** — `postinstall` runs `brokre-setup-mcp`: detects **installed** IDEs only (app, CLI, or real usage artifacts — not empty folders) and merges the MCP entry above into each global config. Idempotent; preserves your other MCP servers.\n3. **Binary auto-upgrade** — on each MCP start, if `PATH` or `~/.brokre/bin/brokre` is older than the npm package, the matching [GitHub Release](https://github.com/Furowu/brokre/releases) is downloaded.\n\n**IDEs covered by auto-setup**\n\n| IDE | Global config |\n|-----|----------------|\n| Cursor | `~/.cursor/mcp.json` |\n| VS Code / Insiders | `…/Code/User/mcp.json` |\n| Claude Code | `~/.claude.json` |\n| Claude Desktop | `…/Claude/claude_desktop_config.json` |\n| Trae | `…/Trae/User/mcp.json` |\n| Kimi Code | `~/.kimi-code/mcp.json` |\n| Windsurf | `~/.codeium/windsurf/mcp_config.json` |\n| OpenClaw | `~/.openclaw/openclaw.json` (`mcp.servers`) |\n\n**Re-run registration** (e.g. you installed brokre before Cursor, then installed Cursor later):\n\n```bash\nbrokre mcp setup              # via CLI — same logic as postinstall\nnpx brokre-setup-mcp          # via npm\nbrokre mcp setup --dry-run    # preview only\nbrokre mcp setup --force      # overwrite existing brokre entry\n```\n\nSkip auto-registration on `npm install`: `BROKRE_MCP_SKIP_SETUP=1`. Disable binary auto-download: `BROKRE_SKIP_AUTO_INSTALL=1`. Pin a binary: `BROKRE_BIN=/path/to/brokre`.\n\nRequires **Node.js 18+** for the npm path.\n\n### Path B — Native CLI (install.sh / Homebrew, no npm)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/Furowu/brokre/main/install.sh | bash\n```\n\n```bash\nbrew tap Furowu/brokre\nbrew install brokre\n```\n\n**Version and upgrade** (built into the CLI — no npm required):\n\n```bash\nbrokre version                # version, binary path, install type\nbrokre version --check        # compare with latest GitHub release\nbrokre version --check --json\nbrokre upgrade                # download latest release (curl + tar)\nbrokre upgrade --check        # exit 1 if an update is available\nbrokre upgrade 0.2.10         # install a specific version\nbrokre upgrade --force        # reinstall even when up to date\n```\n\nRe-run `install.sh` also upgrades when a newer release exists.\n\n**Register MCP after CLI install** (needs Node for the setup script, or `npx` fallback):\n\n```bash\nbrokre mcp setup\n```\n\n### Manual per-IDE setup (optional)\n\nUse this only if you skip auto-setup or need project-scoped config.\n\n**Cursor** — [one-click install](cursor://anysphere.cursor-deeplink/mcp/install?name=brokre&config=eyJicm9rcmUiOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJicm9rcmVAbGF0ZXN0Il19fQ==), or `~/.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"brokre\": { \"command\": \"npx\", \"args\": [\"-y\", \"brokre@latest\"] }\n  }\n}\n```\n\n**Claude Code** — user scope in `~/.claude.json` or `claude mcp add --scope user brokre -- npx -y brokre@latest`. Project scope: `.mcp.json` with `\"type\": \"stdio\"`.\n\nMore clients and env vars: [packages/brokre-mcp/README.md](packages/brokre-mcp/README.md). [MCP Registry](https://registry.modelcontextprotocol.io) ID: `io.github.Furowu/brokre`.\n\n### MCP tools and usage\n\n| MCP tool | Purpose |\n|----------|---------|\n| `brokre_list` | Saved local aliases by default; set `include_bastions=true` to discover routed aliases (`b150::db`), which may require bastion unlock; includes `access`/`availability`/`bastion_gate` |\n| `brokre_exec` | Run **any** saved CLI alias (`binary` + `args`); `ssh` supports `shell_command` for remote scripts; `ssh` + `sudo`/`su` auto-reuses elevated session |\n| `brokre_exec_elevated` | Remote privileged command (`alias`, `command`, `mode`); default `session=reuse` (10 min idle timeout) |\n| `brokre_setup` | Open manage UI in browser for the human to add creds |\n| `brokre_audit_list` | Query audit history (metadata only — args redacted) |\n| `brokre_audit_verify` | Verify tamper-evident audit log chain |\n| `brokre_bastion_policy` | Read or set bastion gate mode (`default` / `strict`); returns `key_set`, `unlocked` |\n\n#### MCP vs CLI (essential for AI agents)\n\nbrokre is **not** a drop-in for `ssh` / `mysql` — you must prefix with `brokre` for vault injection.\n\n| Task | MCP (in IDE) | CLI (terminal / debugging) |\n|------|--------------|----------------------------|\n| List aliases | `brokre_list` | `brokre list --json` |\n| SSH remote command | `brokre_exec` `binary=ssh`, `args=[\"prod\",\"uname\",\"-a\"]` | `brokre ssh prod uname -a` |\n| Any CLI | `brokre_exec` `binary=mysql`, `args=[\"prod-db\",\"-e\",\"SHOW TABLES\"]` | `brokre mysql prod-db -e \"SHOW TABLES\"` |\n| Write remote script | `shell_command=\"…\"` (ssh only) | `brokre ssh prod sh -c '…'` (whole script as one `-c` arg) |\n| Privileged exec | `brokre_exec_elevated` `command=\"…\"` | `brokre ssh prod sudo …` (MCP has session pool; CLI uses fresh PTY each time) |\n| Add credentials | `brokre_setup` (opens browser) | `brokre manage --open` |\n| First-time save | **not available** (human TTY required) | `brokre ssh user@10.0.0.1` |\n\n**Common mistakes (AI agents)**\n\n| Wrong | Right |\n|-------|-------|\n| `ssh prod uptime` | `brokre ssh prod uptime` |\n| MCP `args=[\"prod\",\"uname -a\"]` (one shell string) | `args=[\"prod\",\"uname\",\"-a\"]` (argv tokens) |\n| MCP `args=[\"prod\",\"sh -c 'echo hi'\"]` | `shell_command=\"echo hi\"` or `args=[\"prod\",\"sh\",\"-c\",\"echo hi\"]` |\n| bare `mysql -h … -p` | `brokre mysql <saved-alias> …` |\n| `brokre ssh alias sudo -n …` (sudo-rs, etc.) | `brokre ssh alias sudo …` or `brokre_exec_elevated` — **never** `-n` |\n| `brokre ssh` inside heredoc eats script input | After upgrade, probe commands (`test`/`uname`/`bash -c`) **auto-disconnect stdin**; use `-n`/`--no-stdin` to force |\n| `brokre ssh alias bash -c 'echo $1' _ val` (`$1` empty) | `bash -c \"'echo \\\"\\$1\\\"'\" _ val` (nested quotes), or `shell_command` / env vars |\n\nFor remote SSH: tokens after the alias are **argv slices**, not one shell command. Use split tokens for simple commands; use `shell_command` for complex scripts.\n\n**stdin / stdout (automation scripts)**\n\n- **Intent-aware routing**: non-consumer remote commands (`test`, `uname`, `mkdir`, `bash -c '…'`, etc.) **auto-disconnect stdin** (no `-n` in heredocs); consumer commands (`cat`, `tee`, `tar`, `dd`, `mysql`, `psql`, bare `bash`/`sh`) **forward stdin** — `cat file | brokre ssh … 'cat > path'` works with zero extra flags.\n- Non-upload remote commands use pipe mode by default (stable stdout); MCP `brokre_exec` always disconnects stdin.\n- Custom tool reading stdin: `brokre ssh --with-stdin alias /path/to/tool`.\n- Explicit disconnect: `brokre ssh -n alias cmd`, `brokre ssh --no-stdin alias cmd`.\n- Revert to legacy behavior: `BROKRE_SSH_LEGACY_STDIN=1` (optionally with `BROKRE_SSH_AUTO_NULL_STDIN=1`).\n- **sudo**: use `brokre ssh alias sudo cmd` or MCP `brokre_exec_elevated`; `sudo -n` on sudo-rs is non-interactive and brokre cannot inject a password.\n\n#### MCP elevated sessions (`sudo` / `su`, Unix)\n\nBy default, `brokre mcp` reuses a background elevated shell per `(alias, mode, user)` so sudo passwords are not re-prompted on every call.\n\n**`brokre_exec_elevated`** (preferred for privilege escalation):\n\n```json\n{\n  \"alias\": \"prod\",\n  \"command\": \"systemctl status nginx\",\n  \"mode\": \"sudo_login\",\n  \"session\": \"reuse\"\n}\n```\n\n| Field | Description |\n|-------|-------------|\n| `mode` | `sudo`, `sudo_login` (or `sudo-i`), `su` |\n| `session` | `reuse` (default), `new` (close old session and open fresh), `close` (end session; pass `command: \"\"`) |\n| `user` | `su` mode only; default `root` |\n\nWhen the session pool is enabled, responses include `session_reused` and `session_idle_expires_at` in addition to `exit_code` / `stdout` / `stderr`. `session_idle_expires_at` is a **rolling idle-window hint** refreshed on each call, not a fixed expiry timestamp. `stderr` is usually empty on the pool path. **`stdout` is the remote command output only** — TTY echo, `__BROKRE_*` framing, and shell prompts are stripped.\n\n**`brokre_exec`**: `binary=ssh` with `sudo`/`su` in `args` auto-uses the same pool (always `reuse`; no `session=new|close`). Example: `args=[\"prod\",\"sudo\",\"whoami\"]`.\n\n**Writing remote scripts/files** (`shell_command`, `binary=ssh` only): pass only the alias in `args`; put the full shell script in `shell_command` (brokre normalizes to `sh -c`). Do not embed `sh -c '...'` in `args` or split `printf`/redirects across argv tokens. For privileged system paths use `brokre_exec_elevated.command`.\n\n```json\n{\n  \"binary\": \"ssh\",\n  \"args\": [\"prod\"],\n  \"shell_command\": \"cat > /tmp/deploy.sh <<'EOF'\\n#!/bin/sh\\necho ok\\nEOF\"\n}\n```\n\nBastion routes work the same: `args=[\"b150::db\"]` with `shell_command` as the remote script.\n\n#### SessionRelay tunnel (default)\n\n`brokre ssh b150::db` uses the SessionRelay path by default: the laptop starts `brokre tunnel agent --stdio` on `b150` over SSH, and the agent runs `brokre ssh db` on the bastion. Multi-hop routes peel one hop per agent (`b1::b2::db` starts on `b1`, then `b1` continues with `b2::db`). Inner credentials stay in the bastion vault; the laptop only relays terminal bytes. `BROKRE_TUNNEL=0` is a temporary legacy escape hatch for emergency rollback.\n\n```bash\nbrokre tunnel doctor b150\nbrokre ssh b150::db uname -a\n```\n\nMCP `brokre_exec` responses for routed SSH include `tunnel: { mode, active }`. TcpForward, endpoint sync, Manage UI tunnel controls, and persistent `tunneld` are later phases.\n\n| Control | Default |\n|---------|---------|\n| Idle teardown | 10 minutes |\n| Max lifetime | 30 minutes |\n| Per-command timeout | 120 seconds |\n\n| Variable | Default | Meaning |\n|----------|---------|---------|\n| `BROKRE_MCP_SESSION` | `1` | `0` disables the pool; falls back to one-shot subprocess exec |\n| `BROKRE_MCP_SESSION_IDLE_SECS` | `600` | Idle timeout (seconds) |\n| `BROKRE_MCP_SESSION_MAX_SECS` | `1800` | Max session lifetime (seconds) |\n| `BROKRE_MCP_SESSION_CMD_TIMEOUT` | `120` | Remote command timeout (seconds) |\n\nNot supported: interactive `sudo -i` without a command, `vim`/`top`, or sudo passwords different from the vault `password` field. See [THREAT_MODEL.md](THREAT_MODEL.md) T12.\n\nOn first connect with an **empty vault**, brokre opens **manage** in your browser (`http://127.0.0.1:56777/?t=…`). Session tokens stay on localhost — never returned to the AI. Set `BROKRE_MCP_NO_AUTO_OPEN=1` to disable auto-open.\n\n| MCP tool | Purpose |\n\n### Add credentials\n\nAfter CLI install, the manager opens on first run (`brokre manage --onboard --open`). Or anytime:\n\n```bash\nbrokre manage --open\n```\n\nOr save on first interactive connection (any CLI):\n\n```bash\nbrokre ssh root@10.0.0.1\nbrokre my-tool --host internal.corp\n```\n\n### Use (AI-safe)\n\n```bash\nbrokre mysql prod-db -e \"SHOW TABLES\"\nbrokre ssh prod-bastion uname -a\nbrokre <your-cli> <alias> [args...]\n```\n\n### List metadata (safe for AI / scripts)\n\n```bash\nbrokre list --json              # local aliases with reachability status (default probe)\nbrokre list --no-probe --json   # metadata only, skip TCP/SSH probes\nbrokre list --reachable-only    # hide unavailable aliases\nbrokre list --all --json        # compat: show all (same as default unless --reachable-only)\nbrokre list --no-bastion-discovery   # local only — no bastion SSH discovery\n```\n\nWhen bastions are registered, `brokre list` **stays local-only by default** and does not SSH to bastions or trigger bastion unlock. Use `brokre list --include-bastions` (MCP: `include_bastions=true`) when you actually need routed aliases such as `b150::db`; that remote discovery may require unlock.\n\nOpenSSH sessions inject `ConnectTimeout=5` by default (`BROKRE_SSH_CONNECT_TIMEOUT`). Override with `-o ConnectTimeout=N` or the env var. Bastion RPC (`brokre list --include-bastions` / remote list) times out after 60s (`BROKRE_BASTION_RPC_TIMEOUT`). Remote commands use `ControlPath=none` so they do not attach to a ControlPersist mux master.\n\n### Cross-network list inheritance (bastion broker)\n\nFor **cross-network** access — travel, VPN, public entry points — when direct LAN aliases are unreachable locally but reachable via a bastion running brokre.\n\n**Prerequisites**\n\n1. Laptop: `brokre bastion enable b150` (`b150` is a saved SSH alias)\n2. Bastion host runs brokre at `~/.brokre/bin/brokre` (standard install / `npx` path) with inner aliases saved (e.g. `db`)\n\n**Routed list**\n\n```bash\nbrokre bastion unlock            # if bastion key is set\nbrokre list --include-bastions   # includes b150::db (route=b150, access=via_b150)\n```\n\nWhen cross-network, use `--include-bastions` when you need routed aliases. Plain `brokre list` stays local-only and does not unlock or SSH to bastions.\n\n**Execute**\n\n```bash\nbrokre ssh b150::db uname -a\n# MCP: brokre_exec binary=ssh, args=[\"b150::db\", \"uname\", \"-a\"]\n```\n\nWhen both paths work, the list shows **both** `db` (`direct`) and `b150::db` (`via_b150`) — distinguish by `access`.\n\n### Bastion proxy (cross-network / intranet entry)\n\nPromote **any** saved SSH alias whose remote host runs brokre into a bastion broker. Secrets stay on the bastion; the laptop caches metadata and executes via SSH passthrough.\n\n```bash\nbrokre bastion enable b150        # register ssh alias b150 as bastion\nbrokre bastion set-key              # set bastion unlock key (TTY)\nbrokre bastion unlock               # unlock outbound session (TTL, 30 min idle default)\nbrokre list --include-bastions --json # discover bastion-routed aliases\nbrokre ssh b150::db uname -a        # routed exec via b150 remote brokre\nbrokre bastion sync b150 --json     # fetch alias list from one bastion\n```\n\n- Route separator **`::`** (`:` is illegal in alias names): `db` (local), `b150::db` (via bastion), `b1::b2::inner` (multi-hop, default depth ≤2).\n- **Remote brokre**: routed exec invokes `~/.brokre/bin/brokre` on the bastion (with `BROKRE_SOFT_MEMLOCK=1`, `BROKRE_ALLOW_FILE_KEYCHAIN=1`, `BROKRE_ROUTED_INNER=1` for headless Linux). Interactive commands (e.g. `sudo -i`) automatically get `-tt`.\n- **Guardrails**: probe concurrency cap, ms timeouts, short cache, loop detection, audit `route`/`bastion` (HMAC v4).\n- **Manage UI**: `brokre manage` **Bastion** tab — register/disable bastions, Web set-key and unlock/lock, strict-mode toggle, sync remote aliases; non-TTY still auto-opens `/bastion-auth`. Audit tab filters by `bastion`/`source` and shows route fields.\n\n### Bastion gate policy (default vs strict)\n\nThe bastion **gate** is a human unlock step before sensitive operations. It is **off until you set a bastion key** (`brokre bastion set-key`). After a key exists, unlock establishes a local TTL session (`brokre bastion unlock`, TTY passphrase, manage UI `/bastion-auth`, or MCP browser elicitation).\n\nPolicy is stored at `~/.brokre/bastion/policy.json` (`strict_mode`, default `false`).\n\n| Mode | `gate_mode` | When unlock is required (key must be set) |\n|------|-------------|-------------------------------------------|\n| **default** | `default` | **Bastion outbound only** — `b150::inner` routed exec; SSH/scp/sftp to a **registered** bastion alias; `brokre list` / `brokre_list` when bastion discovery SSHs to bastions. Purely **local** exec (e.g. `brokre ssh lan-db`) does **not** require unlock. |\n| **strict** | `strict` | **Every** `brokre exec` / MCP `brokre_exec`; metadata-only `brokre list` stays local-only, while `--include-bastions` still requires unlock. Use when you want a human in the loop for agent execution while a bastion key is configured. |\n\n**Examples (default mode, key set, session locked)**\n\n| Operation | Unlock needed? |\n|-----------|----------------|\n| `brokre ssh prod uname -a` (local alias) | No |\n| `brokre ssh b150::db uname -a` | Yes |\n| `brokre ssh b150 uptime` (registered bastion) | Yes |\n| `brokre list --include-bastions` | Yes |\n| `brokre list` (local only) | No |\n\nIn **strict** mode, exec rows require unlock; metadata-only `brokre list` remains local-only and does not unlock unless `--include-bastions` is used.\n\n**Set gate mode**\n\n```bash\nbrokre bastion strict status    # default | strict\nbrokre bastion strict on        # strict — all exec gated; list stays local-only unless discovery is requested\nbrokre bastion strict off       # default — bastion outbound only\n```\n\n| Surface | How |\n|---------|-----|\n| CLI | `brokre bastion strict on\\|off\\|status` |\n| Manage UI | **Bastion** tab — strict mode toggle |\n| MCP | `brokre_bastion_policy` — read current policy, or pass `{\"strict_mode\": true}` / `false` |\n\nMCP read response includes `strict_mode`, `gate_mode`, `key_set`, `unlocked`. List/exec responses include `bastion_gate` (`required`, `unlocked_during_call`, `idle_expires_at`).\n\n**Unlock session TTL** (defaults): **30 min idle** (`BROKRE_BASTION_IDLE_SECS`), **8 h max lifetime** (`BROKRE_BASTION_MAX_SECS`). Idle window **renews** on each gated call while unlocked (shared across CLI, MCP, and manage UI via `~/.brokre/run/bastion_session.json`). Bastion gate auth is independent of manage UI session idle expiry. Disable auto-open browser on MCP unlock: `BROKRE_BASTION_NO_AUTO_OPEN=1`.\n\n### Reveal / delete (human-only, real TTY)\n\n```bash\nbrokre reveal mysql prod-db --field password\nbrokre rm ssh prod-bastion\n```\n\n### Audit log (metadata only)\n\n```bash\nbrokre audit list --profile ssh --action exec --json\nbrokre audit verify --json\n```\n\nEvents are stored at `~/.brokre/audit/audit.log` (HMAC-chained). Command arguments are uniformly redacted as `<REDACTED>`. New events include a `source` field (`cli`, `mcp`, or `manage`). The manage UI **Audit log** tab and MCP `brokre_audit_list` expose the same metadata.\n\n### Manage UI security\n\n- **127.0.0.1** only; session token in terminal\n- Passwords: create / rotate only — no read API\n- Delete / rotate require reveal passphrase (or `YES` for auto-saved records)\n- 15-minute idle timeout\n\n## Architecture\n\n```\n┌─────────┐     ┌──────────┐     ┌─────────────┐     ┌────────────┐\n│ AI/User │────▶│ brokre CLI│────▶│ OS Keychain │────▶│ Vault File │\n└─────────┘     └──────────┘     └─────────────┘     └────────────┘\n                      │\n                      ▼\n               ┌─────────────┐\n               │  PTY + inj. │──▶ any CLI on PATH (ssh, mysql, gsql, …)\n               └─────────────┘\n```\n\n- **Double encryption**: unique DEK per field; wrapped for `exec` and `reveal` separately.\n- **Vault metadata**: `profile`, `name`, `host_alias`, `saved_args` in cleartext beside ciphertext ([THREAT_MODEL.md](THREAT_MODEL.md) T3).\n- **SSH private keys**: `0600` temp file + `-i` for the session ([docs/HARDENING.md](docs/HARDENING.md)).\n\n## Preset manage UI groups\n\nConvenience tabs when the binary is on `PATH`:\n\n| Group | Binaries |\n|-------|----------|\n| SSH | `ssh`, `scp`, `sftp` (shared creds) |\n| FTP | `ftp`, `lftp` |\n| MySQL | `mysql`, `mariadb` |\n| PostgreSQL | `psql`, `postgres` |\n| Redis | `redis-cli`, `redis` |\n| ClickHouse | `clickhouse-client`, `clickhouse` |\n| MinIO | `mc`, `minio` |\n\n## Roadmap\n\n**Today:** generic PTY wrapper + `manage.toml` groups + `prompts.toml` overrides.\n\n**Planned:** full TOML connector profiles under `~/.brokre/profiles/` with per-tool injection strategies.\n\n## Piped stdin and OpenSSH sharing\n\n- **Piped stdin** (`tar | brokre ssh host 'tar xf -'`): pipe data forwards only after injection completes.\n- **OpenSSH family** (`ssh`, `scp`, `sftp`): shared saved credentials when the host matches. Interactive save required first (TTY).\n\n## Development\n\n```bash\ncargo test    # unit tests in src/ only (no tests/ integration suite in this repo)\ncargo clippy --all-targets --all-features -- -D warnings\ncargo build --release   # binary: target/release/brokre\n```\n\nRelease version is declared in [`VERSION`](VERSION) (also reflected in `Cargo.toml` and `packages/brokre-mcp/package.json`). Official binaries and npm packages are published by [TechinOne](https://www.tio.tech) via GitHub Releases and CI — not part of this open-source tree.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n---\n\n[Techinone](https://www.tio.tech) · 成都同创合一科技有限公司\n",
  "bytes": 28319,
  "sha": "cfe8499a56387d39c93f82ce88112466ac939abbbfa8f37a9aeca0a630c5a0e8",
  "repo_slug": "furowu/brokre",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_furowu_brokre_23c85056/readme"
}