# Builder decision rules (/en/docs/platform/features/builders/permissions-policy/how-it-decides)



The Permissions-Policy builder suggests every grant from rules you can check. Knowing them tells you when to trust a default and when to overrule it.

## From a report to a grant [#from-a-report-to-a-grant]

The builder reads two report types. Each report names a feature and who asked for it, and the builder turns that into the grant that would allow it:

| Browser reported                                                                                                                               | Suggested grant                                                                 |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| A [Permissions-Policy violation](/en/docs/web-security/reporting-api/reports/permissions-policy-violation): one of your pages used the feature | `self`                                                                          |
| A `potential-permissions-policy-violation`: an iframe from another origin asked for the feature in its `allow` attribute                       | The frame's origin, such as `"https://pay.example.com"`, flagged **From frame** |
| A potential violation from a frame with no origin (a relative `src` or a `srcdoc` frame), or from your own origin                              | `self`                                                                          |

The builder grants origins, not full URLs. A frame from `https://pay.example.com/checkout?step=2` becomes a grant to `https://pay.example.com`.

Both report types can be forged by anyone who knows your endpoint. A feature name the header grammar does not allow, or a frame `src` that is not a plain `http` or `https` origin, never becomes a row, so it cannot reach a generated header.

### Frames and potential violations [#frames-and-potential-violations]

An iframe asks for a feature in its `allow` attribute. The browser reports that request as a potential violation, which the [Permissions Policy reports](/en/docs/platform/monitoring/permissions-policy) page shows in its **Potential** view:

```html title="product.html"
<iframe src="https://video.example.com/embed/42" allow="fullscreen; autoplay"></iframe>
```

This frame produces one row under `fullscreen` and one under `autoplay`, both for `https://video.example.com`. Delegating a feature takes both halves: the header grants it to the frame's origin, and the iframe keeps naming it in `allow`. For the header and attribute model, refer to [the iframe allow attribute](/en/blog/permissions-policy-explained#how-it-relates-to-the-iframe-allow-attribute). When you grant a feature to a frame origin, the deploy step shows **Frames need their allow attribute too** as a reminder.

### Third-party scripts [#third-party-scripts]

For a feature your own pages used, the reports name the script that made each call. The builder sorts each script into one of three groups: your own code (served from your website's origin), another origin's script, or a browser extension.

When every call came from another origin's script or an extension, the `self` row is flagged **Third-party script** and starts rejected. Granting the feature to `self` would hand it to that script too, because a script runs with the permissions of the page that loads it. When at least one call came from your own code, the row starts granted.

## Noise [#noise]

Noise is a reported row that your site does not need. The builder flags a row as noise in either of these cases:

* It has fewer than 10 reports in the period.
* Every call behind it came from a browser extension, which runs on the visitor's machine, not on your site.

The details panel also notes when only one browser reported a row while your site sees several, but that signal never makes a row noise on its own. Only Chromium browsers send these reports, so most sites see a single browser.

Noise starts rejected, but it is only a default. A rarely used page, such as a yearly campaign, can report a feature fewer than 10 times and still need it. Check the **Noise** filter before you deploy.

## Starting presets [#starting-presets]

Browsers never report the Permissions-Policy a page was served with, so the builder cannot detect the policy you send today. It starts from one of these:

| Starting policy                         | Value                                                                                                  |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| **Deny every listed feature** (default) | Every feature of the [catalog](#feature-catalog) set to `()`                                           |
| **Payment page baseline**               | `payment=(self)` and `publickey-credentials-get=(self)`, every other catalog feature set to `()`       |
| **Allow camera and microphone**         | `camera=(self)`, `microphone=(self)`, and `fullscreen=(self)`, every other catalog feature set to `()` |
| **Your own policy**                     | The value you paste, up to 16,384 characters                                                           |

A grant from the starting policy stays granted, even when the reports flag it. The review then adds what your site was reported using.

### When you paste your own policy [#when-you-paste-your-own-policy]

The builder reads the value as a Permissions-Policy header value, without the header name:

* Every feature it sets is kept, including features outside the catalog.
* In each allowlist, it keeps `self`, `src`, `*`, and plain `http` or `https` origins, and drops anything else.
* It drops parameters such as `report-to`, and ignores a member that is not `feature=allowlist`.
* A catalog feature your policy does not set keeps the browser default, unless the reports or your decisions add it.

A pasted policy lives only in the open page. After a reload, the builder falls back to **Deny every listed feature**.

## Feature catalog [#feature-catalog]

The builder lists 25 features, the ones broadly shipped and meaningful to lock down. The presets set all of them, so the header denies every one you do not grant:

| Feature                     | What it lets a page do                                                                               |
| --------------------------- | ---------------------------------------------------------------------------------------------------- |
| `accelerometer`             | Read the motion sensor behind tilt and shake gestures                                                |
| `autoplay`                  | Start video and audio without the visitor pressing play                                              |
| `browsing-topics`           | Share the interests the browser inferred from the visitor's history with ad scripts (the Topics API) |
| `camera`                    | Use the device's camera, once the visitor allows it                                                  |
| `clipboard-read`            | Read what the visitor copied to the clipboard                                                        |
| `clipboard-write`           | Write to the clipboard, as a Copy button does                                                        |
| `display-capture`           | Capture the screen or a window, as screen sharing does                                               |
| `encrypted-media`           | Play DRM-protected video or audio through Encrypted Media Extensions                                 |
| `fullscreen`                | Show an element full screen, such as a video player or a gallery                                     |
| `gamepad`                   | Read game controllers                                                                                |
| `geolocation`               | Read the visitor's location, once they allow it                                                      |
| `gyroscope`                 | Read the device's orientation sensor                                                                 |
| `idle-detection`            | Detect when the visitor is idle or has locked the screen                                             |
| `local-fonts`               | List and use the fonts installed on the visitor's device                                             |
| `magnetometer`              | Read the device's compass sensor                                                                     |
| `microphone`                | Use the device's microphone, once the visitor allows it                                              |
| `midi`                      | Talk to MIDI devices such as music keyboards                                                         |
| `payment`                   | Open the browser's own checkout sheet (the Payment Request API)                                      |
| `picture-in-picture`        | Play a video in a floating window above other apps                                                   |
| `publickey-credentials-get` | Sign the visitor in with a passkey or a security key                                                 |
| `screen-wake-lock`          | Keep the screen on, such as while a boarding pass is shown                                           |
| `serial`                    | Talk to serial devices plugged into the computer                                                     |
| `storage-access`            | Let embedded third-party content ask for access to its own cookies                                   |
| `usb`                       | Talk to USB devices plugged into the computer                                                        |
| `xr-spatial-tracking`       | Run virtual and augmented reality sessions (WebXR)                                                   |

A reported feature outside the catalog gets its own group in the review table too, after the catalog features. It ends up in the header either granted or as `()`.

## How the header is written [#how-the-header-is-written]

The builder writes the policy as one comma-separated line:

* Features come in catalog order, then any other feature in alphabetical order, so the header reads the same from any start.
* A feature with nothing granted is written `()`, which denies it everywhere, including in frames.
* `self` comes first in an allowlist, then each origin in double quotes.
* A feature granted to `*` is written `*` alone, whatever else it lists.
* No `report-to` parameter is written. The reports go to the `default` endpoint of the `Reporting-Endpoints` header, which the builder declares next to `centralcsp`.

For example, a policy that grants `payment` to your pages and to a payment frame, and denies the rest, reads like this, shortened here to five features:

```http
Permissions-Policy-Report-Only: accelerometer=(), autoplay=(), browsing-topics=(), camera=(), payment=(self "https://pay.example.com")
```

The real header lists every catalog feature. For the full example, refer to [Get started](/en/docs/platform/features/builders/permissions-policy/get-started#4-deploy-in-report-only-mode).

## Periods and limits [#periods-and-limits]

The builder reads up to the last 30 days of reports. A website that sends a very large volume of reports is limited to 7 days at a time.

On such a website, the **Period** step says **This website sends a lot of reports, so the builder reads only its last 7 days**, and the **14 days** and **30 days** options are hidden. If a period holds more reports than the builder can read in time, the page shows **This period holds too many reports to analyze in time. Pick a shorter period.** Pick fewer days and continue.

The API applies the same rules. The **Build a recommended Permissions-Policy** endpoint defaults to the last 7 days and covers up to 30. Without a starting policy, it starts from **Deny every listed feature**. Each user can call it 10 times per minute. Refer to the [API reference](/en/docs/api-mcp/api).

## Next steps [#next-steps]

* [Review table](/en/docs/platform/features/builders/permissions-policy/review-features)
* [Get started](/en/docs/platform/features/builders/permissions-policy/get-started)
* [Permissions Policy reports](/en/docs/platform/monitoring/permissions-policy)
* [Permissions-Policy violation report](/en/docs/web-security/reporting-api/reports/permissions-policy-violation)
* [Permissions-Policy reference](/en/docs/web-security/policies/permissions-policy)
