# PlatformPolicy Schema

Platform-scoped governance ceilings applied during the promotion gate.

Source: https://mandrel-web-363efb.gitlab.io/docs/spec/platform-policy/

---


A `MandrelPlatformPolicy` defines the global governance ceilings for the Mandrel mesh. It ensures that no individual agent spec can exceed the platform's risk tolerance, even if the agent developer declares higher limits.

The policy is applied by the [Metrology Lab](https://mandrel-web-363efb.gitlab.io/docs/reference/metrology-lab.md) during the promotion gate using a **min(spec, platform)** strategy for numeric fields and a **deny-priority** strategy for model restrictions.

## Schema

```yaml
apiVersion: mandrelproject.ai/v1
kind: MandrelPlatformPolicy
metadata:
  name: global-policy
spec:
  identity:
    trust-domain: prod.example.internal
    federated-trust-domains:
      - partner.example
  runtime:
    max-hops-allowed: 5
    max-fan-out: 10
    timeout-seconds: 60
  token-budget:
    max-primary-chips: 1000.0
    denied-models:
      - "untrusted-model-v1"
```

### `spec.identity`

Workload identity facts for this deployment. These belong to the SPIRE server or CA that
issues SVIDs, not to any agent, which is why they live here rather than in an authored spec.

| Field | Required | Description |
| :--- | :--- | :--- |
| `trust-domain` | yes | The deployment's SPIFFE trust domain. Path-only references in an agent spec resolve against it, and `tag:spiffe-auth` resolves to a wildcard over the whole domain |
| `federated-trust-domains` | no | Domains whose bundles this deployment has exchanged, and whose SVIDs it can therefore validate |

Keeping the trust domain here is what lets one reviewed spec move between environments. An
agent author knows their workload is `/ns/finance/sa/ledger`; they do not know whether it will
run in `staging.acme.corp` or `prod.example.internal`.

`federated-trust-domains` is a security control, not bookkeeping. A spec naming an absolute
SPIFFE ID outside this list is **rejected at promotion**, because without federation there is
no bundle exchange, without a bundle the SVID cannot be validated, and the rule could never
fire. Rejecting turns a permanently dead allow-list entry into a named error.

### `spec.runtime`

Defines ceilings for execution constraints.

| Field | Type | Description |
| :--- | :--- | :--- |
| `max-hops-allowed` | integer | Absolute ceiling for delegation depth. |
| `max-fan-out` | integer | Absolute ceiling for concurrent delegation width. |
| `max-delegations.per-turn` | integer | Absolute ceiling for delegation volume per turn. |
| `max-delegations.per-session` | integer | Absolute ceiling for delegation volume per session. |
| `timeout-seconds` | integer | Absolute ceiling for execution deadline. |

### `spec.token-budget`

Defines ceilings for financial governance.

| Field | Type | Description |
| :--- | :--- | :--- |
| `max-primary-chips` | number | Absolute ceiling for the primary chip budget (tenths precision). |
| `denied-models` | array of string | List of model IDs that are strictly forbidden on the platform. |

### `spec.intent-classifier`

Defines the platform-scoped default for the Isolated Intent Classifier used for Layer 0 enforcement.

| Field | Type | Description |
| :--- | :--- | :--- |
| `model-id` | string | The trusted model used for classification. |
| `service-url` | string | Optional endpoint for an external classification service. |
| `temperature` | number | Precision/creativity setting (0.0 to 2.0). |

## The "Baking" Process

When an agent is promoted, the Metrology Lab "bakes" the effective values into the **Realized Spec**:

1.  **Numeric Ceilings**: For fields like `timeout-seconds`, the realized value is `min(agent_spec, platform_policy)`.
2.  **Absence is a request for infinity**: if the agent omits a governed field entirely and the platform declares a ceiling, the ceiling is **injected**. This is the rule most easily got wrong. Enforcement reads an absent constraint as *no limit*, so a lab that only bakes values down when the agent's exceeds the ceiling would let an omitted field grant **more** authority than a declared large one. The rule applies to every governed field, not an enumerated subset.
3.  **Model Restrictions**: If the agent's `model-id` appears in `denied-models`, the promotion is rejected with an **L001** error.
4.  **Workload References**: Path-only references and `tag:spiffe-auth` are resolved against `spec.identity.trust-domain`. An absolute reference naming a domain outside `federated-trust-domains` is rejected.
5.  **Intent Classifier**: The platform's `intent-classifier` is injected at `mandrelproject.ai/intent-classifier` when the agent does not declare one. Whether an agent may substitute its own is governed by `agent-override`, which defaults to `forbidden`.
6.  **Warnings**: If a value was reduced or injected, a **L004** (runtime) or **L005** (token-budget) warning is emitted during promotion.

The general rule, for a field this page has not enumerated: **anything reducible is baked,
anything categorically denied is rejected.** A ceiling is reducible — 500 becomes 100 and the
spec still means something. Membership of a deny-list is not; a forbidden model has no smaller
version.

## Administration

The platform policy is managed by platform administrators via the CLI:

```bash
# Get the current policy
mandrel-cli platform policy get > policy.yaml

# Update the policy
mandrel-cli platform policy put policy.yaml
```

Changes to the platform policy do not automatically update existing Realized Specs. Agents must be re-promoted to pick up new policy changes.

