---
title: Platform-Managed Policy | Keycard
description: Govern what coding agents may execute from the Keycard platform: a per-user access policy the keycard CLI enforces on every tool call, centrally visible and auditable instead of buried in per-machine settings files.
---

Every coding agent ships its own permission system, configured in a settings file on the engineer’s machine. That leaves the controls on agent actions invisible to the people accountable for them: you can’t tell what any given agent is allowed to run, whether it matches what your organization approved, or what it actually did.

Platform-managed policy moves those controls into Keycard. The [access policy](/admin/access-policies/index.md) that governs what an agent may execute is stored on the platform and bound to the user running the session. The `keycard` CLI downloads it at session start, enforces it locally on every agent tool call, and records each decision against the session.

## Distributed enforcement points

Keycard stores policy in one place and enforces it at distributed enforcement points. Those points sit at two distinct moments in an agent’s work:

| Enforcement point | The question it answers                                                            | Where the decision is made                                                          |
| ----------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Issuance time     | Which credentials may this agent obtain, for which Resources, and on whose behalf? | The Keycard platform, on every [credential](/concepts/credentials/index.md) request |
| Execution time    | Which tools and commands may this agent run on the machine?                        | The `keycard` CLI, on every tool call the agent executes locally                    |

The `keycard` CLI is the execution-time enforcement point, and this page covers that layer. Its policies decide whether a tool call runs, not which credentials the agent can obtain. The two layers are complementary: issuance-time policy can withhold a GitHub credential from an agent entirely, while execution-time policy decides whether that agent may run `gh pr merge` with a credential it already holds. Both are Cedar and both live in the platform’s [access policy](/admin/access-policies/index.md) system, so they are versioned, integrity-hashed, and auditable the same way.

Execution-time enforcement covers locally executed tools only

The CLI governs what the agent runs on the machine, shell commands, file edits, and the calls it makes out to MCP servers. Agents also reach tools they don’t execute themselves: remote MCP servers, internal APIs, and other agents. Those are governed at issuance time instead, and the check runs on **every interaction with the tool**, not once at session start.

Two ways to get there: build a [Keycard SDK](/sdk/mcp/index.md) into the tool provider, or proxy the upstreams through the [Unified Access Gateway](/admin/unified-access-gateway/index.md). Either way the tool holds **no standing credentials**. It asks Keycard for access on every call, and Keycard can deny it there.

The governance properties of the two layers differ today. Issuance-time policy is administered entirely by platform administrators through the [access policy](/admin/access-policies/index.md) system and cannot be influenced by end users, including from inside an agent session. Execution-time policy is authored by the engineers running those sessions, as described below.

## A drop-in replacement for built-in agent permissions

The controls this replaces are the agent’s own: Claude Code’s [permission modes](https://code.claude.com/docs/en/permission-modes) and allow/deny rules, and their equivalents in other agents.

The primary reason to replace them is the trust boundary. Those controls belong to the harness, so the same software that runs the model also decides what the model may do and how that decision reaches you. The agent shapes what an approval prompt says, how a command is described, and what context you see before you approve it. The thing being governed owns the governance.

Keycard evaluates policy outside the harness. The rules come from the platform, the CLI makes the decision, and neither the model nor the harness can change the ruleset, the verdict, or how it is put to you.

Moving the ruleset off the machine adds the properties that make it governable at scale:

- **Transferable**: one ruleset follows an engineer across machines, projects, and agents, instead of being re-created in each tool’s own configuration format and drifting between laptops, container, remote instances.
- **Centrally visible**: you can see which policies exist, which version each user is enforcing, and every allow, deny, and prompt decision in the [session audit trail](/admin/audit-log-and-sessions/index.md).
- **A foundation for governance**: the primitives here (immutable versions, platform-held bindings, audited decisions) are the ones organization-level policy builds on, where an organization sets a baseline that users can narrow but not widen.

Enforcement is not advisory

The CLI enforces policy through the agent’s pre-tool-use hook, and a hook decision is final: the agent’s own permission system is never consulted. Flags like `--dangerously-skip-permissions` have no effect on the outcome. See [Control Tool Calls](/guides/control-tool-calls/index.md) for the mechanics.

## How enforcement works

Execution-time policy is scoped to a **user**, not to a Zone. Engineers need different permissions from one another, and the ruleset reflects that: every user has their own policy set with its own active binding on the platform. The Zone’s active policy set, the one you manage in Console, is the issuance-time ruleset. It is a separate binding and is not what the CLI evaluates on tool calls.

Nobody creates a user’s ruleset by hand. The first time a session asks for it, the platform materializes one from the Keycard-shipped **baseline** tool-use policy, forked into policies that user owns. From then on, `keycard run` fetches that user’s bundle at session start: the Cedar policies plus the schema they were validated against. It verifies the bundle’s integrity, caches it, and evaluates every tool call locally against it, with nothing to install or configure on the machine.

| Concept            | What it is                                                                         |
| ------------------ | ---------------------------------------------------------------------------------- |
| User policy set    | The execution-time ruleset for one user, with its own active binding               |
| Baseline           | The default tool-use policy Keycard ships. Every user’s set starts as a fork of it |
| Policy set version | An immutable, integrity-hashed snapshot of the set                                 |
| Policy             | An individual Cedar document, versioned on its own                                 |
| Bundle             | What the CLI downloads: the active version’s policies plus the Cedar schema        |

Policies in a set are evaluated together as one Cedar authorization: a tool call is allowed only if some `permit` matches and no `forbid` matches. An `@itl("prompt")` annotation on a `permit` adds a third outcome, where the call is allowed but only after the engineer approves it in the loop. An enforced policy can therefore allow, deny, or prompt. See [Access Policies](/admin/access-policies/#policy-language/index.md) for the Cedar syntax.

## Prerequisites

- The `keycard` CLI installed on the machines running agents (see [CLI](/cli/index.md))
- A Zone the user can sign in to. Their execution-time policy is materialized from the baseline on first use, so there is nothing to provision per user.

## Confirm a session picks up the policy

Run this yourself to see what an engineer’s session does with their policy.

1. **Start a session**

   Terminal window

   ```
   keycard run claude
   ```

   `keycard run` signs in if needed, fetches your user’s current policy version, integrity-checks it, and enforces it on every tool call. Nothing is configured per machine. The bound policy applies automatically at start, and the version is pinned for the life of the session, so a session started before a change keeps enforcing the version it began with.

2. **Ask what is being enforced**

   From inside the session, ask the agent something like *“What policy is being enforced right now?”* The Keycard plugin surfaces the active policies, their versions, and the resolving policy set version, which is how anyone can confirm what a machine is actually enforcing.

3. **Trigger a governed tool call**

   Run something a `permit`, `forbid`, or `@itl` rule targets and confirm the CLI allows, denies, or prompts as expected. The decision appears in the session’s audit trail in the Console alongside the policy set version that produced it.

Caution

Enforcement fails closed. If the platform is unreachable with no cached policy, or the cache fails its integrity check, tool calls are denied.

## Who changes policy today

Policy edits are conversational and happen inside a session. An engineer describes the change they want, such as *“Allow running `npm test` without prompting”*. The agent proposes an updated policy, and on approval it validates the change (Cedar syntax locally, and against the schema on the platform) and publishes it as a new version of that engineer’s policy set.

Three consequences matter when you are assessing this model:

- A change applies to the engineer who made it and to no one else. Users can only read and write their own policy set, so one person widening their ruleset cannot loosen anyone else’s.
- The corollary is the current governance gap: there is no Zone-wide execution-time ruleset, so a user can widen their own policy away from the baseline. Organization-level policy that users can narrow but not widen is what closes this.
- Every change is an immutable, integrity-hashed version with an audit event, so you can reconstruct who changed what and which version was enforced at any point in time. Reverting a user returns their set to the baseline it was forked from.

Note

If a user’s active policy changed since their session started, the publish is rejected and the agent shows a diff to reconcile before republishing. Concurrent edits can’t silently overwrite each other.

## Troubleshooting

- **Every tool call is denied.** Enforcement is failing closed. The platform may be unreachable with no cached policy, or the cache may be corrupt. Reconnect and start a new session so `keycard run` can re-fetch.
- **A change was not published.** The user’s active policy changed since the session started. Ask the agent to reconcile against the diff it shows and publish again.
- **`keycard run` cannot load the policy.** The session may lack a valid token for the policy resource. Confirm it is targeting the right Zone. `keycard run` re-establishes sign-in when needed.
