-
Notifications
You must be signed in to change notification settings - Fork 33
PER-14853: Add HTTP Egress Proxy customer documentation #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| --- | ||
| title: Authorization & Trust | ||
| sidebar_label: Authorization & Trust | ||
| description: How egress rules, Permit.io policy, and human consent compose — including per-human trust ceilings that cap how much access a person can delegate to an agent. | ||
| sidebar_position: 5 | ||
| --- | ||
|
|
||
| # Authorization & Trust | ||
|
|
||
| An allowed outbound request passes through **two independent gates**. Both must say yes. This mirrors how the gateway authorizes MCP tool calls, so the same mental model — and the same Permit policy environment — governs both planes. | ||
|
|
||
| ## The two gates | ||
|
|
||
| ```mermaid | ||
| flowchart TB | ||
| Req["Outbound request"] | ||
| Rule{"1. Egress rule<br/>host / path / method"} | ||
| Permit{"2. Permit policy<br/>agent's role on the host"} | ||
| Ceiling{"Trust ceiling<br/>(delegated agents)"} | ||
| Allow["Forwarded"] | ||
| Deny["Denied"] | ||
|
|
||
| Req --> Rule | ||
| Rule -->|"no match or block"| Deny | ||
| Rule -->|"allow"| Permit | ||
| Permit -->|"deny"| Deny | ||
| Permit -->|"allow"| Ceiling | ||
| Ceiling -->|"over ceiling"| Deny | ||
| Ceiling -->|"within ceiling"| Allow | ||
| ``` | ||
|
|
||
| ### Gate 1 — Egress rules | ||
|
|
||
| The [egress rules](./egress-rules) you author are the coarse "is this host/path/method even on the menu" check. A request with no matching `allow` rule is denied before any policy evaluation. This is the operator-controlled allow-list. | ||
|
|
||
| ### Gate 2 — Permit.io policy | ||
|
|
||
| For requests that an `allow` rule permits, the gateway then asks your **Permit.io** policy whether *this agent* is allowed to perform *this kind of action* on *this host*. The request's method class maps to an action (read, write, delete, and so on), and the host becomes a Permit resource. Because every gateway host maps 1:1 to a Permit environment, you manage these policies in the same place as your MCP policies — see [Permit.io Integration](/permit-mcp-gateway/permit-integration). | ||
|
|
||
| This split means an operator can broadly allow a host at the proxy while your central policy still decides, per agent, what that host's access actually permits. | ||
|
|
||
| ## Human consent and trust ceilings | ||
|
|
||
| Agents usually act **on behalf of a human**. The proxy supports the same delegation-with-consent model the gateway uses for MCP: a person explicitly authorizes an agent to make egress calls for them, and the access an agent gets through that person is capped by a **trust ceiling**. | ||
|
|
||
| ### Trust ceilings | ||
|
|
||
| An admin sets, per human and per host, the **maximum** trust level that person is allowed to delegate — `low`, `medium`, or `high`. When a human consents to an agent, the access that agent receives is the **lesser** of what the human grants and the admin-defined ceiling. A person can never delegate more than their ceiling allows. | ||
|
|
||
| This has an important security property: an agent that acts for **several** humans does **not** get to combine their ceilings. Each human caps their own delegation independently, so one person's grant can't be used to escalate another's. | ||
|
|
||
| ### Human consent | ||
|
|
||
| To have a human delegate egress access to an agent, run the consent flow: | ||
|
|
||
| ```bash | ||
| asg proxy authorize <agent-client-id> --host api.github.com | ||
| ``` | ||
|
|
||
| This opens a browser where the human signs in and chooses what to allow: | ||
|
|
||
| 1. **Sign in** — the human authenticates. | ||
| 2. **Review the hosts** — the agent is requesting egress to specific hosts. | ||
| 3. **Choose an access level** — defaulting to least privilege, and capped at the admin's ceiling for that human and host. | ||
| 4. **Approve** — a short-lived, human-bound token is issued for the agent. | ||
|
|
||
| Pass multiple `--host` flags to request several hosts at once. The optional `--trust low|medium|high` flag lets the human (or the operator preparing the request) propose a lower cap than the ceiling; omit it to default to least privilege. | ||
|
|
||
| The result is a token whose access is bound to that specific human's consent — and which the human can revoke. | ||
|
|
||
| ### Revocation | ||
|
|
||
| A human can revoke an agent's delegated access at any time from their account. Revocation is enforced **immediately**: in-flight and subsequent requests using a revoked delegation are denied, not just future token issuance. This is the egress equivalent of revoking an MCP consent. | ||
|
|
||
| ## How automated agents differ | ||
|
|
||
| Not every agent acts for a human. A CI job or backend service can be issued a token directly with [`asg proxy token create`](./quickstart#4-mint-an-agent-access-token). Such a token isn't bound to a human, so the per-human trust ceiling doesn't apply — its access is governed purely by the egress rules and the agent's own Permit policy. Use direct tokens for machine-to-machine automation, and the consent flow whenever a real person is accountable for what the agent does. | ||
|
|
||
| --- | ||
|
|
||
| ## What's next | ||
|
|
||
| - [**Egress Rules**](./egress-rules) — the operator allow-list that forms the first gate. | ||
| - [**Permit.io Integration**](/permit-mcp-gateway/permit-integration) — the policy model behind the second gate. | ||
| - [**Security**](./security) — the protections that wrap both gates. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| --- | ||
| title: The asg CLI | ||
| sidebar_label: asg CLI | ||
| description: Install and use the asg command-line tool — the operator's front door for enabling the HTTP Egress Proxy, managing rules and credentials, minting agent tokens, and wiring agents into the proxy. | ||
| sidebar_position: 2 | ||
| --- | ||
|
|
||
| # The `asg` CLI | ||
|
|
||
| `asg` is the command-line tool for managing the Permit MCP Gateway, including the entire HTTP Egress Proxy control plane. Anything you can do in the **Proxy** section of the [dashboard](https://app.agent.security) — enable the proxy, manage rules and credentials, mint tokens, run diagnostics — you can do with `asg`, scripted and CI-friendly. | ||
|
|
||
| The CLI is the operator and automation surface. End users who simply delegate access to an agent normally do that through the browser consent flow, not the CLI. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| npm install -g @permit-io/agent-security-cli | ||
| ``` | ||
|
|
||
| Verify the install: | ||
|
|
||
| ```bash | ||
| asg --help | ||
| asg proxy --help | ||
| ``` | ||
|
|
||
| ## Configure | ||
|
|
||
| `asg` resolves settings in priority order: environment variables, then a config file at `~/.agent-security/`, then built-in defaults. | ||
|
|
||
| ```bash | ||
| # Point at your gateway and store an admin token (saved with restrictive permissions). | ||
| asg config set gateway-url https://gateway.example.com | ||
| asg login --token <admin-token> | ||
|
|
||
|
|
||
| # Verify the token against the gateway. | ||
| asg whoami | ||
| ``` | ||
|
|
||
| You can also supply settings through environment variables, which is the preferred approach in CI: | ||
|
|
||
| | Variable | Purpose | | ||
| | --- | --- | | ||
| | `AGENT_SECURITY_API_KEY` | Admin token used to authenticate control-plane commands. | | ||
| | `AGENT_SECURITY_GATEWAY_URL` | Your gateway's admin URL. | | ||
| | `AGENT_SECURITY_SUBDOMAIN` | Default host subdomain, so you can omit it from each command. | | ||
| | `AGENT_SECURITY_CONSENT_URL` | Consent origin used by `asg proxy authorize` (usually auto-derived). | | ||
| | `AGENT_SECURITY_PROXY_TOKEN_FILE` | Path to the agent's proxy token file (set by `asg proxy env`). | | ||
|
|
||
| :::warning Treat the admin token as a high-value secret | ||
| Prefer the interactive prompt or `AGENT_SECURITY_API_KEY` over passing the token as a `--token` flag — command-line flags are visible to other users on the machine via the process list. The CLI scrubs secret-shaped strings from its own error output, but the process list is outside its control. | ||
| ::: | ||
|
|
||
| ## Output and exit codes | ||
|
|
||
| Every command supports machine-readable output so agents and CI can drive it deterministically: | ||
|
|
||
| ```bash | ||
| asg proxy rules list acme --json --quiet | ||
| ``` | ||
|
|
||
| - `--json` emits structured JSON to stdout. | ||
| - `--quiet` drops decorative output (spinners, hints). | ||
| - `--yes` accepts confirmation prompts non-interactively. | ||
|
|
||
| Exit codes are stable: | ||
|
|
||
| | Code | Meaning | | ||
| | --- | --- | | ||
| | `0` | Success | | ||
| | `1` | User error — bad input, missing token, cancelled prompt, or a human-denied approval | | ||
| | `2` | Server error — gateway unreachable, 5xx, or network failure | | ||
|
|
||
| ## The `asg proxy` command tree | ||
|
|
||
| | Command | What it does | | ||
| | --- | --- | | ||
| | `asg proxy config <host> --enable\|--disable` | Turn proxy mode on or off for a host. | | ||
| | `asg proxy rules list\|add\|update\|delete\|reorder` | Manage egress rules. See [Egress Rules](./egress-rules). | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Problem: This row lists Suggestion: Document only |
||
| | `asg proxy credentials list\|create\|update\|delete` | Manage stored upstream secrets (write-only). See [Credentials](./credentials). | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Problem: This row documents Suggestion: Remove the |
||
| | `asg proxy connect <provider>` | Connect an OAuth provider (`github_app`, `notion`, `google`, `atlassian`) and store the resulting credential. | | ||
| | `asg proxy token create <host> --client-id <id>` | Mint a short-lived agent proxy token (`--out <file>` writes it securely). | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Problem: This row shows Suggestion: Document |
||
| | `asg proxy authorize <client_id>` | Run the browser consent flow so a human can delegate access to an agent within a trust ceiling. | | ||
| | `asg proxy preset list\|apply` | Browse and apply preset rule + credential packs for common providers. | | ||
| | `asg proxy env <host>` | Print the `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` environment for the current shell. | | ||
| | `asg proxy container-config <host>` | Emit Docker, Docker Compose, or Kubernetes ConfigMap bootstrap config. | | ||
| | `asg proxy ca status\|download\|rotate\|trust-shell` | Manage the per-host TLS-interception certificate authority. See [Connecting Agents](./connecting-agents#https-interception). | | ||
| | `asg proxy doctor <host>` | Diagnose proxy setup end to end. | | ||
|
|
||
| Run `asg proxy <command> --help` for the full flag list of any command. | ||
|
|
||
| ## Passing the host subdomain | ||
|
|
||
| Most commands take the host subdomain as a positional argument (e.g. `asg proxy config acme`). If you've set a default with `asg config set subdomain acme` (or `AGENT_SECURITY_SUBDOMAIN`), you can omit it. Commands that already take another positional accept the host as a `--subdomain` flag instead. | ||
|
|
||
| ## Shell completions | ||
|
|
||
| Enable tab-completion for your shell: | ||
|
|
||
| ```bash | ||
| asg completion --shell zsh >> ~/.zshrc # or: --shell bash >> ~/.bashrc | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## What's next | ||
|
|
||
| - [**Egress Rules**](./egress-rules) — the rule model in depth. | ||
| - [**Credentials & Connections**](./credentials) — static secrets, OAuth connections, and request signing. | ||
| - [**Connecting Agents**](./connecting-agents) — `asg proxy env`, `container-config`, and `doctor` in context. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| --- | ||
| title: Connecting Agents | ||
| sidebar_label: Connecting Agents | ||
| description: Route an agent's outbound traffic through the HTTP Egress Proxy — shell, Docker, and Kubernetes setup, the proxy access token, HTTPS interception, and the doctor diagnostic. | ||
| sidebar_position: 6 | ||
| --- | ||
|
|
||
| # Connecting Agents | ||
|
|
||
| The proxy uses the standard `HTTP_PROXY` / `HTTPS_PROXY` convention, so most HTTP client libraries route through it automatically once the environment is set. Connecting an agent has two parts: **the environment variables** that point traffic at the gateway, and **a proxy access token** the agent presents to authenticate. | ||
|
|
||
| ## The pieces | ||
|
|
||
| | Piece | What it is | | ||
| | --- | --- | | ||
| | `HTTP_PROXY` / `HTTPS_PROXY` | Point your agent's outbound HTTP and HTTPS at the gateway. | | ||
| | `NO_PROXY` | Hosts that should bypass the proxy (e.g. localhost). | | ||
| | Proxy access token | A short-lived token the agent presents to the proxy. Minted with [`asg proxy token create`](./quickstart#4-mint-an-agent-access-token) or the [consent flow](./authorization#human-consent), and referenced via a token file. | | ||
|
|
||
| You rarely set these by hand — `asg proxy env` and `asg proxy container-config` generate them for you. | ||
|
|
||
| ## Shell / local process | ||
|
|
||
| ```bash | ||
| eval "$(asg proxy env acme)" | ||
| ``` | ||
|
|
||
| This exports `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, and the path to the agent's token file into the current shell. Any process launched from that shell now routes its outbound traffic through the proxy. Mint the token first (step 4 of the [Quick Start](./quickstart)). | ||
|
|
||
| ## Docker and Kubernetes | ||
|
|
||
| Generate ready-to-use container configuration: | ||
|
|
||
| ```bash | ||
| # Docker env file or Compose snippet. | ||
| asg proxy container-config acme --format docker-env | ||
| asg proxy container-config acme --format docker-compose | ||
|
|
||
| # Kubernetes ConfigMap. | ||
| asg proxy container-config acme --format k8s-configmap | ||
| ``` | ||
|
|
||
| Mount or reference the emitted configuration in your agent's container so the proxy variables are present in its environment. Supply the proxy token through your platform's normal secret mechanism (a mounted file or secret), and point `AGENT_SECURITY_PROXY_TOKEN_FILE` at it. | ||
|
|
||
| ## HTTPS interception | ||
|
|
||
| By default the proxy handles HTTPS in **passthrough** mode: it authenticates and applies host-level rules, but the encrypted tunnel is forwarded opaquely — the proxy does not see the request path or body, and it can't inject a credential or scrub the response inside the tunnel. | ||
|
|
||
| To apply **full** governance to HTTPS — path-level rules, credential injection, response scrubbing, and approval gates inside the encrypted connection — switch the relevant rule to **intercept** mode. You set a rule's TLS mode in the [dashboard](https://app.agent.security) rule editor (**Proxy → Rules**, edit the rule, set **TLS mode** to **Intercept**); the CLI creates rules in passthrough mode. | ||
|
|
||
| In intercept mode the proxy terminates the agent's TLS using a **certificate authority issued for your host** and re-applies every check to the decrypted request. For your agent to trust those connections, it must trust that CA certificate — manage it with the CLI: | ||
|
|
||
| ```bash | ||
| # Inspect your host's interception CA. | ||
| asg proxy ca status acme | ||
|
|
||
| # Download the CA certificate to install in your agent's trust store. | ||
| asg proxy ca download acme --output ./agent-security-ca.pem | ||
|
|
||
| # Print a shell snippet that points common tools at the CA. | ||
| asg proxy ca trust-shell acme | ||
|
|
||
| # Rotate the CA, keeping the previous one valid during a grace window. | ||
| asg proxy ca rotate acme --grace-days 7 | ||
| ``` | ||
|
|
||
| :::note Choosing a mode | ||
| Use **passthrough** when host-level control is enough, or for very large uploads, downloads, and long-lived streaming connections. Use **intercept** when you need path-level rules, credential injection, or response scrubbing on HTTPS traffic. Mode is per rule, so you can mix the two across your rule set. | ||
| ::: | ||
|
|
||
| ## Diagnose with `doctor` | ||
|
|
||
| When something isn't connecting, run the built-in diagnostic: | ||
|
|
||
| ```bash | ||
| asg proxy doctor acme | ||
| ``` | ||
|
|
||
| `doctor` checks, end to end: | ||
|
|
||
| - Gateway reachability | ||
| - Whether the proxy is enabled for the host | ||
| - How many rules and credentials are configured | ||
| - Your local proxy environment variables | ||
| - The interception CA's status and expiry, and whether your environment trusts it | ||
| - The token file's permissions and the token's expiry | ||
|
|
||
| Each check reports a clear pass/fail with a suggested fix, so it's the first thing to run whenever an agent's traffic isn't behaving. | ||
|
|
||
| --- | ||
|
|
||
| ## What's next | ||
|
|
||
| - [**Quick Start**](./quickstart) — the end-to-end setup these commands fit into. | ||
| - [**Security**](./security) — what the proxy enforces on the traffic you route through it. | ||
| - [**The `asg` CLI**](./cli) — full command reference. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| --- | ||
| title: Credentials & Connections | ||
| sidebar_label: Credentials & Connections | ||
| description: Store upstream API secrets in the gateway and inject them into outbound requests server-side — static keys, OAuth connections, and AWS request signing — so your agents never hold the credentials they use. | ||
| sidebar_position: 4 | ||
| --- | ||
|
|
||
| # Credentials & Connections | ||
|
|
||
| The proxy can attach an upstream API's credential to outbound requests **server-side**, so your agents call third-party APIs without ever holding the keys. You store the secret once in the gateway; an [egress rule](./egress-rules) names it; the proxy injects it on the way out and [scrubs it](./security#response-scrubbing) from the response on the way back. | ||
|
|
||
| This is one of the proxy's most important properties: **the secret never leaves the gateway**. It isn't in the agent's prompt, environment, or memory, so it can't leak through logs or be exfiltrated by prompt injection. | ||
|
|
||
| ## Credentials are write-only | ||
|
|
||
| Once stored, a secret can never be read back through the API, the CLI, or the dashboard — only **overwritten** or **deleted**. Listing credentials shows their names, target hosts, and whether a secret is set — never the secret value itself. This holds for everyone, including admins. | ||
|
|
||
| ## Static credentials | ||
|
|
||
| A static credential is a fixed secret (an API key, bearer token, or basic-auth password) that you supply. You choose **how** it's injected: | ||
|
|
||
| | Injection method | What it does | | ||
| | --- | --- | | ||
| | **`header`** | Adds a request header. You set the header name and a value template containing `{secret}` (e.g. `Authorization: Bearer {secret}`). | | ||
| | **`query`** | Adds a query-string parameter. You set the parameter name. | | ||
| | **`basic_auth`** | Sets HTTP Basic authentication from a username plus the stored secret. | | ||
|
|
||
| ```bash | ||
| # Bearer token in an Authorization header. | ||
| asg proxy credentials create stripe acme \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Static-credential example uses the non-existent Problem: Both static-credential examples here invoke Suggestion: Present static-credential creation as a dashboard task (Proxy → Credentials), keep the injection-method table as conceptual reference, and reserve the CLI examples for |
||
| --host api.stripe.com \ | ||
| --injection header \ | ||
| --header-name Authorization \ | ||
| --value-template "Bearer {secret}" \ | ||
| --secret-file ./stripe.key | ||
|
|
||
| # API key as a query parameter. | ||
| asg proxy credentials create weather acme \ | ||
| --host api.weather.example \ | ||
| --injection query \ | ||
| --param-name apikey \ | ||
| --secret-file ./weather.key | ||
| ``` | ||
|
|
||
| Each credential is **bound to one or more hosts** — it's only ever injected on requests to those hosts, so a credential for one API can never be sent to another. | ||
|
|
||
| :::tip Supply secrets without exposing them | ||
| Prefer `--secret-file` (or piping the secret in) over `--secret` on the command line, so the value doesn't land in your shell history or the process list. | ||
| ::: | ||
|
|
||
| ## OAuth connections | ||
|
|
||
| For providers that use OAuth, you don't paste a static token — you run a browser-based connect flow that completes the OAuth handshake and stores the resulting grant. The proxy then injects a valid access token on each request and handles refresh for you. | ||
|
|
||
| Supported providers: | ||
|
|
||
| - `github_app` | ||
| - `notion` | ||
| - `google` | ||
| - `atlassian` | ||
|
|
||
| ```bash | ||
| asg proxy connect github_app acme \ | ||
| --key github \ | ||
| --client-id <oauth-client-id> \ | ||
| --client-secret-file ./github-client-secret \ | ||
| --host api.github.com | ||
| ``` | ||
|
|
||
| This opens a browser to authorize the connection, then stores the credential under the key you chose (`github` above) — ready to be referenced from an [egress rule](./egress-rules). | ||
|
|
||
| **Dashboard:** **Proxy → Credentials** lets you create OAuth connections and static credentials through a guided form. | ||
|
|
||
| ## AWS request signing | ||
|
|
||
| For AWS APIs, the proxy can sign outbound requests with **AWS SigV4**, so your agents can call AWS services without ever holding AWS keys. Rather than storing a long-lived access key, the gateway mints **temporary credentials** (via STS `AssumeRole`) and signs each request server-side. | ||
|
|
||
| You configure AWS request signing in the [dashboard](https://app.agent.security) under **Proxy → Credentials** as an AWS credential, then bind it to the relevant AWS host(s) like any other credential. | ||
|
|
||
| ## Preset packs | ||
|
|
||
| Presets bundle a ready-made set of rules plus a credential placeholder for a common provider, so you can wire up a known API in one step instead of authoring rules by hand. Browse and apply them: | ||
|
|
||
| ```bash | ||
| asg proxy preset list | ||
| asg proxy preset apply <preset-name> acme --secret-file ./provider.key | ||
| ``` | ||
|
|
||
| `apply` previews what it will create before writing, so you can review the rules and credential it adds. | ||
|
|
||
| **Dashboard:** **Proxy → Presets**. | ||
|
|
||
| --- | ||
|
|
||
| ## What's next | ||
|
|
||
| - [**Egress Rules**](./egress-rules) — reference a credential from an `allow` rule with `--credential`. | ||
| - [**Security**](./security#response-scrubbing) — how injected secrets are scrubbed from responses. | ||
| - [**Connecting Agents**](./connecting-agents) — route an agent's traffic so injection takes effect. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[MEDIUM]
authorizeuses a required--hostsflag, not repeatable--host;--trustdefault islowProblem: The example
asg proxy authorize <agent-client-id> --host api.github.comand the prose "Pass multiple--hostflags … The optional--trust low|medium|highflag … omit it to default to least privilege" do not match the implementation (agent-security/packages/agent-security-cli/src/commands/proxy.ts,authorizeCommand):--hosts(plural), comma-separated and required (demandOption: true), with per-hosthost:trustsyntax — e.g.--hosts api.github.com:medium,api.openai.com:low. There is no repeatable--host.--trustsets the default level for hosts given without an explicit:trust, and its default value islow(not an unset "least privilege").--consent-url https://<tenant>.agent.security(orAGENT_SECURITY_CONSENT_URL).Suggestion:
Describe
--hostsas required/comma-separated withhost:trust, and--trustas the default for hosts lacking an explicit level (defaultlow).