diff --git a/docs/core/mcp.md b/docs/core/mcp.md index 38eb8dd..37c2f06 100644 --- a/docs/core/mcp.md +++ b/docs/core/mcp.md @@ -102,7 +102,7 @@ happens rather than what the specs allow. | Any client that needs to self-register | yes | Enable `--enable-client-id-metadata-document` (preferred) or `--enable-dynamic-client-registration` (RFC 7591, for clients that predate CIMD) | Both self-registration mechanisms ship in 2.4.0 and are **off by default**. Turn -on the one your client can use — see [Self-registering clients](#self-registering-clients) +on the one your client can use — see [Self-registering clients](#self-registering-clients-cimd-vs-dcr) below. The static-token path remains the simplest option when you control the client and do not want an interactive flow at all. diff --git a/docs/core/metrics-monitoring.md b/docs/core/metrics-monitoring.md index 9c18810..9dbe5df 100644 --- a/docs/core/metrics-monitoring.md +++ b/docs/core/metrics-monitoring.md @@ -170,7 +170,10 @@ The `outcome` label is what makes an intersection denial diagnosable: This is the Confused Deputy being stopped. Do **not** widen the agent; it cannot help, and the user genuinely lacks access. - **`not_enforced`** — a delegated caller arrived but the active model declares - no `agent` type, so the request was authorized as the **user alone**. + no `agent` type, so the agent half of the intersection could not be evaluated. + By default the check is **denied**; with `--fga-allow-unconstrained-agents` + set it is authorized as the **delegating user alone**, with the agent + unconstrained. Only `check_permissions` emits `allowed` / `denied_by_agent` / `denied_by_user`. `list_permissions` intersects object *sets* rather than folding a per-check diff --git a/docs/enterprise/agent-identity.md b/docs/enterprise/agent-identity.md index 92906aa..a696120 100644 --- a/docs/enterprise/agent-identity.md +++ b/docs/enterprise/agent-identity.md @@ -222,9 +222,21 @@ Without this an agent's actions are indistinguishable from the user's own — sa | `allowed` | both halves permitted | — | | `denied_by_agent` | the agent has no grant; the user may well have access | grant the **agent** a tuple | | `denied_by_user` | the agent had its grant, the user does not have access | **do not** widen the agent — this is the Confused Deputy case working as intended | -| `not_enforced` | a delegated caller arrived but the model declares no `agent` type, so it was authorized as the **user alone** | declare `type agent` and grant your agents | +| `not_enforced` | a delegated caller arrived but the model declares no `agent` type, so the agent half could not be evaluated | declare `type agent` and grant your agents | -`not_enforced` is the one to alert on: it is the only outcome that reports a security property *not* being enforced, and it is silent by construction — the request succeeds and nothing in the response says the agent was unconstrained. +`not_enforced` is the one to alert on, and it means one of two things depending +on [`--fga-allow-unconstrained-agents`](#turning-it-on): + +- **Default (flag unset):** the check was **denied**. Agents are failing against + a model that cannot express agent grants — a misconfiguration, not an attack. + Users will report it as a broken integration; this counter tells you why. +- **Flag set:** the check was authorized as the **delegating user alone**, with + the agent unconstrained. That is silent by construction — the request + succeeds and nothing in the response says so. Treat a non-zero rate here as + exposure with a clock on it, not a steady state. + +Either way the remedy is the same: add `type agent` to the model and grant the +agents. `allowed`, `denied_by_agent` and `denied_by_user` are emitted by **`check_permissions` only**. `list_permissions` intersects object *sets*