{
  "markdown": "# infrabroker\n\n[![CI](https://github.com/luisgf/infrabroker/actions/workflows/go.yml/badge.svg)](https://github.com/luisgf/infrabroker/actions/workflows/go.yml)\n[![Release](https://img.shields.io/github/v/release/luisgf/infrabroker)](https://github.com/luisgf/infrabroker/releases)\n[![Go Report Card](https://goreportcard.com/badge/github.com/luisgf/infrabroker)](https://goreportcard.com/report/github.com/luisgf/infrabroker)\n[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](LICENSE)\n[![Docs](https://img.shields.io/badge/docs-luisgf.github.io%2Finfrabroker-informational)](https://luisgf.github.io/infrabroker/)\n\n**Infrastructure access broker for AI agents — SSH & Kubernetes. The model\nnever touches a credential.** *(formerly `ssh-broker`)*\n\nThe agent requests an *action* — run a command on a host, query or change a\ncluster. infrabroker checks it against policy, executes it with a credential\nminted for that single operation — an **ephemeral, scope-limited SSH\ncertificate** from its own CA, or a **short-lived bound ServiceAccount token**\n— and returns **only the output**. Keys, certificates and tokens live in the\nbroker's memory and are discarded after the call: nothing enters the model's\ncontext, so a prompt-injected agent has nothing to exfiltrate.\n\nOne binary — `infrabroker` — exposes the same engine (`internal/broker`) and tool\nsurface (`internal/mcpserver`) over three transports, chosen by subcommand. (The\nlegacy per-transport binaries `broker` / `mcp-broker` / `mcp-broker-http` remain as\nthin **deprecated wrappers** over these subcommands, so existing configs keep\nworking.)\n\n- **MCP stdio (local, recommended for personal use)** — `infrabroker serve-mcp`.\n  Tools: `ssh_execute`, `ssh_session_open` / `ssh_session_exec` / `ssh_session_close`,\n  `ssh_list_servers`, `ssh_put_file` / `ssh_get_file`; with clusters configured,\n  also `k8s_get` / `k8s_list` / `k8s_logs` / `k8s_apply` / `k8s_delete` /\n  `k8s_list_clusters`. No transport auth — isolation comes from the process\n  being launched by the user (as the MCP spec recommends for stdio).\n- **MCP HTTP + OAuth2/OIDC (remote, multi-user)** — `infrabroker serve-mcp-http`,\n  Streamable HTTP. Same tools, but each client authenticates with an **OIDC\n  bearer token** validated locally against the issuer's JWKS; the user identity\n  (and groups, for per-user RBAC) is propagated to the signer.\n- **HTTP + mTLS** — `infrabroker serve-http`, `POST /v1/ssh_run` (one-shot), for\n  network agents authenticated with a client certificate.\n\n## Documentation\n\nThis README is a landing page. The detail lives in focused, single-source docs:\n\n| Document | Contents |\n|---|---|\n| [QUICKSTART.md](docs/QUICKSTART.md) | First `ssh_execute` in under 10 minutes — single-binary local mode, no signer/PKI |\n| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | Diagram, request flow, design decisions, sudo elevation, sessions, multi-CA |\n| [THREAT_MODEL.md](docs/THREAT_MODEL.md) | Actors, trust boundaries, security controls, and explicit non-goals/gaps |\n| [OPERATIONS.md](docs/OPERATIONS.md) | Runbook: startup, adding hosts, hot-reload, `broker-ctl`, PKI rotation, configs |\n| [MESH.md](docs/MESH.md) | Running infrabroker over a NetBird / Tailscale mesh — the session layer on top of the overlay path |\n| [HA.md](docs/HA.md) | Why it is single-instance today: state inventory, the blockers, and what degrades under replication |\n| [API.md](docs/API.md) | HTTP endpoint reference for all services |\n| [USAGE.md](docs/USAGE.md) | Guide to the MCP tools (SSH + Kubernetes), dry-run, and audit review (for the model / operator) |\n| [SECURITY.md](docs/SECURITY.md) | Vulnerability disclosure policy |\n| [CONTRIBUTING.md](docs/CONTRIBUTING.md) · [CODING_STYLE.md](docs/CODING_STYLE.md) | Workflow, versioning, Go style |\n\n## Why infrabroker\n\n- **Anti-exfiltration (prompt injection):** the ephemeral key/cert/token live\n  only in the broker's memory; they never enter the model's context.\n- **Kubernetes without kubeconfigs:** the signer mints a short-lived **bound\n  ServiceAccount token** (TokenRequest API) per operation; every cluster is\n  **default-deny** with per-verb/resource/namespace policy and the same\n  dry-run, approval and audit path as SSH.\n- **Anti-reuse:** each cert carries a TTL of minutes, `source-address` (broker or\n  bastion IP), and — for one-shot — a `force-command`. Useless outside its\n  host/time/IP.\n- **Controlled escalation:** `allow_sudo` / `allowed_sudo_users` live in the\n  signer; a compromised broker cannot escalate where policy forbids it.\n- **CA compromise bounded:** one CA per host group (`ca_keys`), each key\n  optionally in Azure Key Vault or ssh-agent (YubiKey PIV / SoftHSM / TPM)\n  — the private key never leaves the HSM.\n- **Audit / non-repudiation:** append-only, Ed25519-chained log correlated by\n  `serial` across signer, broker, and `sshd`.\n\nThe full threat model — including what the system deliberately does **not**\ndefend — is in [THREAT_MODEL.md](docs/THREAT_MODEL.md).\n\n## How it works\n\n```\nAI model ──tool call──> broker ──mTLS──> [control-plane] ──mTLS──> signer\n   (no credential)      (ephemeral key      (approval +          (CA key +\n                         in RAM, never        guardrails,          policy + RBAC,\n                         on disk)             no CA key)           signs the cert)\n                            │\n                            └── SSH with the ephemeral cert ──> bastion ──> target host\n                                                                 └─ stdout/stderr/exit_code ─> model\n```\n\nThe broker sends an *intent* (`{host, role, purpose, command?, sudo?, pty?,\npubkey, …}`); the signer derives every certificate constraint from policy and\nreturns the signed cert. The ephemeral private key is generated in the broker\nand never leaves it. See [ARCHITECTURE.md](docs/ARCHITECTURE.md) for the request flow,\nthe design decisions, and the per-hop ProxyJump certificate diagrams.\n\n## Feature overview\n\n| Capability | One-liner | More |\n|---|---|---|\n| **Ephemeral certificates** | Ed25519 pair in RAM per operation; minutes-long, scoped cert. No reusable secret. | [ARCHITECTURE](docs/ARCHITECTURE.md) |\n| **External signer** | A separate `cmd/signer` holds the CA key and policy; the broker never does. | [ARCHITECTURE](docs/ARCHITECTURE.md) |\n| **Multi-CA + HSM** | One CA key per host group via `ca_keys`; local PEM, Azure Key Vault, or ssh-agent/HSM. | [ARCHITECTURE](docs/ARCHITECTURE.md#multi-ca--ca-custody-v1110-agent-backend-122) |\n| **AI-action firewall** | Per-host or **composable-by-group** command policy (allow/deny/`require_approval`), POSIX-sh AST parsing, dry-run. Authoritative for one-shot. | [ARCHITECTURE](docs/ARCHITECTURE.md#ai-action-firewall) · [USAGE](docs/USAGE.md) |\n| **Human-in-the-loop approval** | Optional control plane gates `require_approval` commands behind out-of-band approval; the signer enforces it. | [ARCHITECTURE](docs/ARCHITECTURE.md#human-in-the-loop--control-plane) · [API](docs/API.md#control-plane-api) |\n| **Action budgets** (behaviour guardrails) | Budget *how much* an agent can do: per-CN sign-rate cap plus per-subject rate limit and novelty escalation (a subsequent new host / novel command → approval); observe or enforce. Network tools budget what an agent can *reach* or *spend*; this budgets the actions themselves. | [OPERATIONS](docs/OPERATIONS.md#action-budgets-rate-limits--behavior-guardrails) · [ARCHITECTURE](docs/ARCHITECTURE.md#human-in-the-loop--control-plane) |\n| **RBAC** | Broker-CN groups (mTLS) + per-end-user OIDC groups; fail-closed. | [ARCHITECTURE](docs/ARCHITECTURE.md#rbac) |\n| **sudo / PTY** | Policy-gated elevation (`sudo -n`) and PTY allocation, per host. | [ARCHITECTURE](docs/ARCHITECTURE.md#privilege-elevation-sudo-nopasswd) |\n| **Kubernetes broker** | `k8s_*` tools with per-operation bound SA tokens, default-deny verb/resource/namespace policy, dry-run. | [USAGE §10](docs/USAGE.md#10-kubernetes-tools-k8s_) |\n| **Session recording** | `shell`/`pty` sessions to ASCIIcast v2 (`.cast`), indexed by `session_id`. | [USAGE §8](docs/USAGE.md#8-session-recording) |\n| **Chained audit** | Append-only, Ed25519-signed, SHA-256-chained; correlated by `serial`. | [USAGE §7](docs/USAGE.md#7-reviewing-audit-logs) · [API](docs/API.md#audit-log-correlation) |\n| **Hot reload** | `signer.json` re-read (and validated) without restart, via `POST /v1/reload` or SIGHUP. | [OPERATIONS §3](docs/OPERATIONS.md#3-hot-reload) |\n\n## Comparison with existing solutions\n\nSeveral tools address SSH access control or AI-agent credential security, but\nnone cover the full combination that infrabroker targets in a lightweight,\nself-hosted package.\n\n| Feature | **infrabroker** | Teleport | Vault + SSH engine | StrongDM | ssh-mcp |\n|---|---|---|---|---|---|\n| Ephemeral cert in memory (no disk) | ✅ | ✅ | ✅ | ❌ | ❌ |\n| Separate broker / signing service | ✅ | ✅ | Partial | ❌ | ❌ |\n| MCP-native (AI agents) | ✅ | ✅ (2025) | ✅ (2025) | ❌ | ✅ |\n| OAuth2/OIDC on MCP transport | ✅ | ✅ | ✅ | ❌ | ❌ |\n| Per-command policy + dry-run (AI-action firewall) | ✅ | ❌ | ❌ | ❌ | ❌ |\n| Human-in-the-loop approval for AI commands | ✅ | ❌ | ❌ | ❌ | ❌ |\n| Per-agent behavioral guardrails (anomaly/rate) | ✅ | ❌ | ❌ | ❌ | ❌ |\n| Session recording (ASCIIcast v2, stdin+stdout+stderr) | ✅ | ✅ | ❌ | Partial | ❌ |\n| Cryptographically chained audit log | ✅ | ❌ | ❌ | Partial | ❌ |\n| Single-binary / simple self-hosted | ✅ | ❌ | ❌ | ❌ | ✅ |\n| HSM/KMS for CA key | ✅ (AKV) | ✅ | ✅ | — | — |\n\n**[Teleport](https://goteleport.com/)** is the closest commercial equivalent —\nshort-lived SSH certs, RBAC, and since 2025 *Secure MCP*; its Jan-2026 *Agentic\nIdentity Framework* targets the same threat model. The difference is operational\nweight: Teleport needs a dedicated control-plane cluster, recording proxy, and\nweb UI — orders of magnitude heavier than a Go binary + signer.\n\n**[HashiCorp Vault SSH secrets engine](https://developer.hashicorp.com/vault/docs/secrets/ssh)**\nis an SSH CA with full HSM/KMS support and (2025) its own MCP server, but it\nprovides only the *signing* piece — you still build the execution layer\n(`engine.go`, `session.go`, the MCP tools) yourself.\n\n**[StrongDM](https://www.strongdm.com/)** hides credentials but stores\nlong-lived secrets rather than generating ephemeral certs in memory, making it\nweaker against exfiltration. **[Smallstep SSH CA](https://smallstep.com/)** is a\nlightweight OIDC-integrated SSH CA (close to `cmd/signer`) with no execution\nbroker or MCP layer. **[ssh-mcp](https://github.com/tufantunc/ssh-mcp)** exposes\nSSH to LLMs over MCP but uses a **static SSH key** — the exact vulnerability this\nbroker prevents. **[CyberArk PAM](https://docs.cyberark.com/)** offers\ncomparable JIT cert access but is a closed enterprise platform for human\noperators, not AI workloads.\n\n**Where it fits:** MCP-native AI-agent access + in-memory ephemeral certs +\nseparate signer + ASCIIcast recording + chained audit, as a small set of Go\nbinaries without a cluster. Enterprise features (web UI, multi-region HA) are on\nthe roadmap (see [HANDOFF.md](docs/HANDOFF.md)).\n\n## Install\n\n- **Prebuilt binaries** — each [release](https://github.com/luisgf/infrabroker/releases)\n  ships `infrabroker_<ver>_{linux,darwin}_{amd64,arm64}.tar.gz` with all binaries,\n  plus the installer tarball (`infrabroker-v<ver>.tar.gz`) that\n  `deploy/install.sh` consumes for the systemd production path.\n- **go install** — `go install github.com/luisgf/infrabroker/cmd/infrabroker@latest`\n  (pure Go, no CGO; same for the other `cmd/` binaries).\n- **Container** — `ghcr.io/luisgf/infrabroker` (docker or podman, multi-arch;\n  entrypoint is the stdio MCP frontend). See [CONTAINERS.md](docs/CONTAINERS.md),\n  including a compose demo that runs the full stack against a toy host:\n  `cd examples/compose && docker compose up --build -d` (or `make demo`).\n- **From source** — the Quickstart below.\n\nRegister with Claude Code in one line — native binary or container:\n\n```bash\nclaude mcp add infrabroker -- ~/bin/infrabroker serve-mcp -config /secure/path/config.json\nclaude mcp add infrabroker -- docker run -i --rm -v /secure/path:/config \\\n  ghcr.io/luisgf/infrabroker -config /config/config.json\n```\n\n## Quickstart\n\n**Fastest path (local, single binary):** [QUICKSTART.md](docs/QUICKSTART.md) takes\nyou from `git clone` to your first `ssh_execute` in under 10 minutes with one\nbinary and one `config.json` — no signer service, no PKI. The steps below set up\nthe full **remote** stack (a separated signer); the containerised demo is under\n[Install](#install).\n\n```bash\n# 1. Build (make injects the version from the git tag into every binary)\nmake install                 # → ~/bin/{infrabroker,signer,broker,broker-ctl,mcp-broker,...}\n# or a single binary:        make signer\n# (plain `go build ./cmd/...` also works; it reports a dev-<commit> version)\n\n# 2. Generate the local PKI + the two-service config (signer.json + config.json)\ninfrabroker init             # writes pki/, signer.json, config.json; --force to redo\n# add --import-ssh-config to import hosts from ~/.ssh/config, --register-mcp to\n# run `claude mcp add` for you\n\n# 3. Start the signing service (must be running before the broker)\n./signer.sh start\n\n# 4. Add a host and reload\nbroker-ctl host add --name web01 --addr web01.example.com:22 --user deploy --scan \\\n  --groups prod-web --sudo\nbroker-ctl reload\n\n# (--config is a global flag, before the subcommand; every binary takes --version)\nbroker-ctl --config /secure/path/signer.json host list\nbroker-ctl --version            # short; add --verbose for build details\n```\n\nRegister the stdio MCP with your client:\n\n```jsonc\n// Claude Code — ~/.claude.json\n\"infrabroker\": { \"type\": \"stdio\", \"command\": \"/Users/<you>/bin/infrabroker\",\n                \"args\": [\"serve-mcp\", \"-config\", \"/secure/path/config.json\"] }\n\n// OpenCode — ~/.config/opencode/opencode.json  (note: type \"local\", command is an array)\n\"infrabroker\": { \"type\": \"local\",\n                \"command\": [\"/home/<you>/bin/infrabroker\", \"serve-mcp\", \"-config\", \"/secure/path/config.json\"],\n                \"enabled\": true }\n```\n\nFull setup — local vs external signing mode, the remote OAuth frontend, host\nfields, sudoers, PKI, and `broker-ctl` — is in [OPERATIONS.md](docs/OPERATIONS.md).\nTool usage for the model is in [USAGE.md](docs/USAGE.md).\n\n## API\n\nFull reference: [API.md](docs/API.md).\n\n| Service | Endpoint | Auth | Description |\n|---|---|---|---|\n| Signer | `POST /v1/sign` | mTLS | Request an ephemeral SSH certificate |\n| Signer | `GET /v1/hosts` | mTLS | List accessible hosts (filtered by caller groups) |\n| Signer | `POST /v1/reload` | mTLS | Hot-reload `signer.json` without restart |\n| Control plane | `POST /v1/sign`, `/v1/approvals/{id}`, … | mTLS | Forwarding + human approval |\n| Broker HTTP | `POST /v1/ssh_run` | mTLS | Execute a one-shot SSH command |\n| MCP HTTP | `/.well-known/oauth-protected-resource` | None | OAuth2 discovery (RFC 9728) |\n| MCP HTTP | Streamable HTTP | OIDC Bearer | MCP tools |\n\n## Security\n\nThe security posture — trust boundaries, the layered controls (RBAC, command\npolicy, approval gate, guardrails, source-address/TTL pinning, chained audit),\nand the **explicit non-goals** (`mode=exec` sessions are broker-preflighted but\nnot host-enforced, no KRL, secrets logged verbatim, …) — is documented in\n[THREAT_MODEL.md](docs/THREAT_MODEL.md).\n\nTo report a vulnerability, see [SECURITY.md](docs/SECURITY.md). CI enforces `gofmt`,\n`go vet`, `go test -race`, and `govulncheck` on every push and PR.\n\n## Testing\n\n```bash\nmake test                      # go test -race ./...  (cert build, policy/RBAC/sudo/PTY, hops, …)\nbash lab/run_signer_lab.sh     # external signer: broker without ca_key + policy + denial\nbash lab/run_mcp_lab.sh        # bastion + target (ProxyJump) MCP scenario\nbash lab/run_lab.sh            # HTTP/mTLS frontend\n```\n\n## License\n\nCopyright (C) 2026 Luis González Fernández.\n\nThis program is free software: you can redistribute it and/or modify it under the\nterms of the **GNU General Public License v3.0** as published by the Free Software\nFoundation. It is distributed in the hope that it will be useful, but WITHOUT ANY\nWARRANTY. See [LICENSE](LICENSE) for the full text.\n",
  "bytes": 16241,
  "sha": "4c6bec45ba5430d793a3d19899898d67bcf297d280e27f2dca11b31dabc14c5f",
  "repo_slug": "luisgf/infrabroker",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_luisgf_infrabroker_b47bd428/readme"
}