# Network access

Browser and generated-server connections begin denied.

## Granting an origin

1. `set_network_access(action: "list", app_id)` — returns the live grants and `policy_version`. Skip this step only for an app you created in this conversation with no grants yet: create_app's response already told you `policy_version` is `0`.
2. Copy that `policy_version` **integer** into `expected_policy_version`. It is a counter, not a version string: a brand-new app is `0`, and it increases by one per change. Sending `"0.0"` is wrong.
3. `set_network_access(action: "grant", app_id, surface, origin, reason, idempotency_key, expected_policy_version)`. The origin must be an exact public HTTPS origin — no path, query, port, wildcard, or IP literal. Server grants also bind `server_methods`. Choose the surface yourself from the app's architecture — server for Worker-side calls like a data sync, browser for fetches the page itself makes; the owner approves or declines at the native consent prompt, so do not ask which surface in chat first.
4. The client asks the owner to confirm; the grant only exists after they accept. An agent can never self-approve, and a client that cannot show a confirmation prompt fails closed.

`NETWORK_POLICY_CONFLICT` means the policy changed underneath you: list again and reuse the new integer. Revoking needs no new permission — same call with `action: "revoke"`.

## What a browser grant does and does not open

A browser grant lets page JavaScript `fetch` the origin and load its images and media. Loading executable scripts, stylesheets, or fonts additionally requires `browser_directives` naming them:

```json
{ "action": "grant", "app_id": "<app_id>", "surface": "browser", "origin": "https://cdn.jsdelivr.net", "browser_directives": ["script"], "expected_policy_version": 0, "reason": "Load the three.js renderer", "idempotency_key": "grant-cdn" }
```

A `script` grant runs third-party code inside the private app with the app's full privileges, and the origin can change that code at any time — the owner is told exactly that before approving. Vendoring the library into the app is usually better: it is one call, it cannot change underneath the app, and it needs no grant. See get_docs(dependencies).

An app must also declare any remote origin it references in `podda.json` `network.expectedOrigins`, or the deploy is rejected with the remedy. Frames, external forms, and service workers stay denied.

Browser revocation applies to newly served HTML; an already loaded page keeps its prior CSP until reload.
