> ## Documentation Index
> Fetch the complete documentation index at: https://keyring.docs.composio.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Origin allowlist

> How Keyring keeps each credential within its approved toolkit destinations and protects token operations.

export const OriginAllowlistDiagram = ({scenario}) => {
  const checks = [{
    key: "https",
    label: "HTTPS only"
  }, {
    key: "public",
    label: "Public address only"
  }, {
    key: "origin",
    label: "In the Gmail allowlist"
  }, {
    key: "token",
    label: "Not a token endpoint"
  }];
  const scenarios = {
    allowed: {
      label: "A Gmail credential travels with a tool call to Keyring. Keyring runs the Gmail toolkit's rules: HTTPS, public address, origin in the Gmail allowlist, and not a token endpoint. All pass, your KMS unwraps the credential, and the request is forwarded to gmail.googleapis.com.",
      target: "→ gmail.googleapis.com",
      destination: "Gmail API",
      host: "gmail.googleapis.com",
      google: true
    },
    toolkit: {
      label: "A Gmail credential travels with a tool call aimed at slack.com. slack.com is an approved origin for the Slack toolkit, but Keyring checks the Gmail toolkit's allowlist because the credential belongs to Gmail. The check fails and the call is denied before decryption; slack.com is never reached.",
      target: "→ slack.com · approved for Slack only",
      destination: "Slack",
      host: "slack.com",
      google: false
    },
    token: {
      label: "A Gmail credential travels with a tool call aimed at Google's OAuth token endpoint. The origin is in the Gmail allowlist, but it is a token endpoint, which ordinary tool calls can never reach. The call is blocked and the endpoint is never reached.",
      target: "→ oauth2.googleapis.com/token",
      destination: "Google OAuth",
      host: "oauth2.googleapis.com/token",
      google: true
    }
  };
  const current = scenarios[scenario];
  return <figure className={`keyring-diagram simple-request-flow origin-flow origin-flow--${scenario}`}>
      <div className="simple-request-flow__stage" role="img" aria-label={current.label}>
        <div className="simple-request-flow__track" aria-hidden="true" />

        <div className="simple-request-flow__nodes">
          <div className="simple-request-flow__node origin-flow__node--composio">
            <span className="simple-request-flow__node-mark simple-request-flow__node-mark--composio" aria-hidden="true" />
            <b>Composio</b>
          </div>
          <div className="simple-request-flow__node origin-flow__node--keyring">
            <span className="simple-request-flow__node-mark simple-request-flow__node-mark--keyring">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M12 3 5 6v5c0 4.5 2.8 7.5 7 9.5 4.2-2 7-5 7-9.5V6L12 3Z" />
                <circle cx="12" cy="11" r="2" />
                <path d="M12 13v3" />
              </svg>
            </span>
            <b>Keyring</b>
            <span className="simple-request-flow__node-caption">Your Cloud</span>
          </div>
          <div className="simple-request-flow__node origin-flow__node--destination">
            {current.google ? <span className="simple-request-flow__node-mark simple-request-flow__node-mark--gmail">
                <svg viewBox="0 0 32 24" aria-hidden="true">
                  <path d="M3 21 V6" />
                  <path d="M3 6 L16 15 L29 6" />
                  <path d="M29 6 V21" />
                  <path d="M3 6 L8 9.5" />
                </svg>
              </span> : <span className="simple-request-flow__node-mark origin-flow__node-mark--globe">
                <svg viewBox="0 0 24 24" aria-hidden="true">
                  <circle cx="12" cy="12" r="8" />
                  <ellipse cx="12" cy="12" rx="3.4" ry="8" />
                  <path d="M4 12h16" />
                </svg>
              </span>}
            <b>{current.destination}</b>
            <span className="simple-request-flow__node-caption">{current.host}</span>
          </div>
        </div>

        <ul className="auth-gate__checks origin-flow__checks" aria-hidden="true">
          <li className="origin-flow__rules">
            <span className="origin-flow__rules-mark">
              <svg viewBox="0 0 32 24" aria-hidden="true">
              <path d="M3 21 V6" />
              <path d="M3 6 L16 15 L29 6" />
              <path d="M29 6 V21" />
              <path d="M3 6 L8 9.5" />
            </svg>
            </span>
            Gmail toolkit rules
          </li>
          {checks.map(check => <li key={check.key} className={`auth-gate__check auth-gate__check--${check.key}`}>
              <span className="auth-gate__mark auth-gate__mark--ok" aria-hidden="true">
                <svg viewBox="0 0 24 24">
                  <path d="m6 12.5 4 4 8-9" />
                </svg>
              </span>
              <span className="auth-gate__mark auth-gate__mark--bad" aria-hidden="true">
                <svg viewBox="0 0 24 24">
                  <path d="m7 7 10 10m0-10L7 17" />
                </svg>
              </span>
              {check.label}
            </li>)}
        </ul>

        <div className="simple-request-flow__kms-link origin-flow__kms-link" aria-hidden="true" />
        <div className="simple-request-flow__kms-packet origin-flow__kms-packet" aria-hidden="true" />
        <div className="simple-request-flow__kms origin-flow__kms">
          <span className="simple-request-flow__node-mark simple-request-flow__node-mark--kms">
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <circle cx="7.5" cy="12" r="3.5" />
              <path d="M11 12h10m-3 0v3m-3-3v2" />
            </svg>
          </span>
          <b>KMS</b>
        </div>

        <div className="origin-flow__packet" aria-hidden="true">
          <div className="origin-flow__payload origin-flow__payload--credential">
            <span className="origin-flow__credential-icon">
              <svg viewBox="0 0 32 24" aria-hidden="true">
                <path d="M3 21 V6" />
                <path d="M3 6 L16 15 L29 6" />
                <path d="M29 6 V21" />
                <path d="M3 6 L8 9.5" />
              </svg>
              <svg className="origin-flow__credential-lock" viewBox="0 0 24 24" aria-hidden="true">
                <rect x="5.5" y="10.5" width="13" height="8.5" rx="2" />
                <path d="M8.5 10.5V8.2a3.5 3.5 0 0 1 7 0v2.3" />
                <path d="M12 13.6v2" />
              </svg>
            </span>
            <span>Gmail credential</span>
            <small>{current.target}</small>
          </div>
          <div className="origin-flow__payload origin-flow__payload--forwarded">
            <span className="origin-flow__credential-icon">
              <svg viewBox="0 0 32 24" aria-hidden="true">
                <path d="M3 21 V6" />
                <path d="M3 6 L16 15 L29 6" />
                <path d="M29 6 V21" />
                <path d="M3 6 L8 9.5" />
              </svg>
              <svg className="origin-flow__credential-lock" viewBox="0 0 24 24" aria-hidden="true">
                <rect x="5.5" y="10.5" width="13" height="8.5" rx="2" />
                <path d="M15.5 10.5V8.2a3.5 3.5 0 0 0-6.5-1.7" />
                <path d="M12 13.6v2" />
              </svg>
            </span>
            <span>Forwarded</span>
            <small>credential added after every check</small>
          </div>
          <div className="origin-flow__payload origin-flow__payload--denied">
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <circle cx="12" cy="12" r="8" />
              <path d="m9 9 6 6m0-6-6 6" />
            </svg>
            <span>{scenario === "token" ? "Blocked" : "Denied"}</span>
            <small>
              {scenario === "token" ? "token endpoints need the exchange route" : "not in the Gmail toolkit's allowlist"}
            </small>
          </div>
        </div>
      </div>
    </figure>;
};

Keyring only allows credentials to be used with approved destinations and defends against
exfiltration attempts. A valid Composio token proves who is calling, but it does not decide where a
credential may go.

Before Keyring asks your KMS to open a credential, the destination must match the policy for that
credential's toolkit.

## Each credential stays with its toolkit

Every credential is sealed for one toolkit. Keyring checks a Gmail credential against Gmail's approved
origins, even if the request names a destination approved for another toolkit.

<OriginAllowlistDiagram scenario="allowed" />

A Gmail tool call to `gmail.googleapis.com` passes the destination checks, so Keyring can ask your KMS
to open the credential and forward the request.

<OriginAllowlistDiagram scenario="toolkit" />

A Gmail tool call to `slack.com` is denied. Slack may be approved for the Slack toolkit, but it is not
an approved destination for a Gmail credential. Keyring makes no KMS call.

## Ordinary tool calls cannot reach token endpoints

Keyring separates ordinary provider requests from token issuance, OAuth refresh, and revocation.

| Route                       | Used for                                | Allowed destinations                                      | Response handling                                            |
| --------------------------- | --------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------ |
| `POST /api/v1/forward/http` | Ordinary tool calls                     | The toolkit's approved origins, excluding token endpoints | Returns the provider response after required checks          |
| `POST /api/v1/exchange`     | Token issuance, refresh, and revocation | Only the toolkit's approved token endpoints               | Encrypts sensitive response fields before they leave Keyring |

<OriginAllowlistDiagram scenario="token" />

A tool call to `oauth2.googleapis.com/token` is blocked on the forward route even though it is a Google
destination. Only the exchange route can reach that endpoint.

This separation matters because a token endpoint can turn a refresh token into a new credential. The
exchange route encrypts fields such as `access_token`, `refresh_token`, `id_token`, and `client_secret`
before returning the response to Composio. If Keyring cannot safely apply those rules, it fails closed.

See [Credential refresh and exchange](/concepts/credential_refresh) for the complete refresh flow.

## Checks applied to every provider request

| Check               | What Keyring enforces                                                                        |
| ------------------- | -------------------------------------------------------------------------------------------- |
| Secure transport    | Production and staging destinations must use HTTPS.                                          |
| Public address      | Local, loopback, link-local, private, reserved, and other non-public addresses are rejected. |
| Toolkit destination | The destination must match the requesting toolkit's approved origin patterns.                |
| Route boundary      | Token endpoints are blocked from ordinary forwarding.                                        |
| Redirects           | Every permitted redirect is checked again before Keyring follows it.                         |

A failed check ends the request before decryption. Keyring never falls back to a broader rule.

## The policy manifest

The official image includes a reviewed manifest with one entry per toolkit. Standard deployments use
this bundled policy automatically.

| Field                | What it controls                                                              |
| -------------------- | ----------------------------------------------------------------------------- |
| `allowed_origins`    | Provider destinations that ordinary tool calls may reach                      |
| `exchange_endpoints` | Token endpoints and the response fields that must be encrypted                |
| `body_secrets`       | Exact endpoints and JSON fields where a provider permits credential placement |

Most providers accept a credential in a header or query parameter. When a provider requires a secret
inside a JSON body, `body_secrets` limits it to reviewed endpoints and fields. Keyring rejects placement
anywhere else.

Do not set `origin_policy.manifest_url` for a standard deployment. A custom policy is useful when you
want to block specific toolkits or operations, or when a provider uses a private or customer-specific
destination. Custom policies should be reviewed before use and should prefer exact provider hosts over
broad wildcards.

See [Network and request policy](/security/network_and_policy) for inbound access, redirect handling,
and the controls you can customize in your deployment.
