# Share a saved aggregate with a browser page

> Review an immutable app-view grant, mint a five-minute browser token on your backend, and stop future access.

Canonical page: https://anectico.com/docs/apps/browser-aggregate-view/


A page you host can read an explicitly approved saved aggregate directly from Anectico. Your backend
keeps the management key and mints a temporary **app_read** token for a signed-in, authorized visitor.
The browser receives only that narrow token, in memory. Your agent builds the page and backend in
your workspace; you host and operate them.

## Ask your agent

Ask your agent to review the project's approved aggregate shares with `list_app_view_grants`,
prepare the complete `create_app_view_grant` preview, or stop a selected share with
`revoke_app_view_grant`. Your agent uses the existing read and internal-write gateways and the CLI;
you review the fixed resources, fields, origins, expiry and external-copy consequence before confirming.
The running page and your token-mint backend use REST, with separate credentials as described below.

## What the owner approves

An **app-view grant** fixes one project, one parent management key, exact result IDs with their
snapshot IDs and content digests, a closed field projection, the exact source-scope ceiling, exact
page origins and a fixed expiry. These fields cannot be edited. Changing one means reviewing a new
grant and revoking the old one.

Only a ready saved trend **without a breakdown** is eligible. Choose one or more closed selectors:

| Selector | Fields shown |
| --- | --- |
| `APP_VIEW_PROJECTION_TREND_SERIES_SUMMARY` | Series IDs, whole-window measurements, contributor and population counts, spans and coverage. |
| `APP_VIEW_PROJECTION_TREND_SERIES_BUCKETS` | Chart bucket ranges and measurements. |
| `APP_VIEW_PROJECTION_TREND_FORMULAS` | Formula IDs, operands, units, coverage and values. |

The interpretation envelope always includes status, computation and expiry times, measurement windows,
coverage, population counts and limits. Definitions, event names, filters, breakdown labels,
participant and selection IDs and snapshot details never reach the browser. This recipe grants no
persons, logs, transcripts, replay, SQL, execution, writes or SSE access.

An origin is an exact `https://host[:port]`, with no path, wildcard or `null`. Local loopback HTTP is
available only on a development deployment. Approve at most five origins, ten results and three
selectors per grant. A grant lasts between five minutes and 90 days, never beyond its parent's
expiry; at most 100 active grants exist per project.

## Separate discovery, sharing and runtime authority

| Step | Credential and exact permissions |
| --- | --- |
| Builder discovery | The usual OAuth connection: `docs:read`, `mcp:read`, `projects:read`, `services:read`. OAuth does not authorize sharing or minting. |
| Provision a grant | An owner or admin session, or an explicitly delegated management builder, with `app_views:share`, `api_key:read`, `api_key:write`, `analytics:read`, `persons:read` and every ceiling and source scope. MCP writes additionally need `mcp:write`; MCP listing needs `mcp:read`. |
| List / revoke | Sharing and key-read permission; revoke also needs key-write permission. Reads alone or mint-only authority cannot control grants. |
| Runtime mint | The exact project management key whose ID equals the grant's `parent_api_key_id`, with `app_views:mint`, `analytics:read`, `persons:read` and every owner-derived source scope. A different key, OAuth session or mint-only key fails. |
| Measurement preparation | Separately authorized analytics credentials: base `analytics:read` and `persons:read`, plus source scopes; preview and execution add `analytics:query`, saved-insight use also `insights:read`, and full preview inspection adds `governance:read`. Definition reads need `insights:read`. The browser token does none of these jobs. |

The parent retains ordinary project and domain read authority. Only the issued browser token is
projection-restricted. Keep the parent in your backend's secret store, separate from the builder's
credential. The closed source ceiling may additionally name `groups:read`, `persons:profile:read`
and `agents:content:read` only when the result owner requires them; approve each explicitly.

## Consent, preview and confirm

In **Privacy → Content policy**, the owner explicitly allows **Aggregate result → App-view sharing**
for the selected project. An ordinary read rule grants no sharing. Missing, unknown or denied consent
refuses sharing. Policy changes use that permissioned control and its reviewed confirmation.
Set consent before preparing the aggregate. A policy revision can withdraw an earlier publication;
prepare an explicitly authorized new result under the current policy instead of reusing an invalidated one.

Read the control contract with `anectico api spec --partition customer --out customer-contract.json`
and the browser protocol with `anectico api spec --partition protocol --out browser-contract.json`.
Pin the serving revision and each artifact's digest. Both are deliberately excluded from the ordinary
apps partition; exclusion does not mean their dedicated credential protocol is absent.
Inspect `get_app_view_grants`, `post_app_view_grants` and `delete_app_view_grants_by_grant_id` in
customer, and `post_app_view_grants_by_grant_id_tokens` and
`get_app_views_by_grant_id_results_by_result_id` in protocol.

Create a grant file with placeholders for the exact already-measured result:

```json
{
  "parent_api_key_id": "PARENT_KEY_UUID",
  "allowed_operations": ["APP_VIEW_OPERATION_AGGREGATE_RESULT_READ"],
  "resources": [{"result_id": "RESULT_UUID", "snapshot_id": "SNAPSHOT_ID", "content_digest": "EXACT_DECIMAL_DIGEST"}],
  "field_projection": ["APP_VIEW_PROJECTION_TREND_SERIES_SUMMARY"],
  "scope_ceiling": ["analytics:read", "persons:read"],
  "origins": ["https://board.example"],
  "expires_at": "FIXED_RFC3339_EXPIRY"
}
```

```bash
anectico --project PROJECT_UUID app-view grant create --file grant.json --idempotency-key board-share-1
# Review every field, parent authority/expiry, sharing policy, source approval and external-copy consequence.
anectico --project PROJECT_UUID app-view grant create --file grant.json --idempotency-key board-share-1 --confirm-token SERVER_TOKEN
anectico --project PROJECT_UUID app-view grant list --limit 50
anectico --project PROJECT_UUID app-view grant list --limit 50 --cursor OPAQUE_NEXT_CURSOR
```

MCP has `list_app_view_grants` through `execute_read_action`, and confirmed
`create_app_view_grant` / `revoke_app_view_grant` through `execute_internal_action`; these add no
top-level tool slots. REST controls are `GET/POST /api/v1/app-view-grants` and
`DELETE /api/v1/app-view-grants/{grant_id}` with exact project and version and the same server preview.

Confirmation binds identical immutable arguments and the complete `expected_admission_state_digest`
shown in preview: current parent authority, sharing-policy version and decision and owner-approved
result, snapshot and projection identity and digest. Adapters carry that verified digest automatically; an
optional CLI `--expected-admission-state-digest` can pin it before preview. Even a policy-version
change that still allows sharing conflicts and creates no active grant. Review again after a change.
Parent facts are fenced at commit; policy and result approval is checked at the immediately preceding
verification answer and rechecked by mint and every read. Identical creation retries keep the same
creation key and return original metadata, including revoked state, with no new authority or secret.

## Mint only on your backend

Mint uses the exact parent key, never a management key supplied by the browser. Your backend signs in
its own visitors and authorizes the fixed grant and result it offers. It exposes a fixed token endpoint,
never a generic project and path proxy. Set `Cache-Control: no-store` and prevent token bodies from
entering access logs, errors, telemetry or a retained response cache.

The CLI requires a new explicit private file, inside an existing protected directory:

```bash
anectico --project PROJECT_UUID app-view token mint --grant GRANT_UUID --result RESULT_UUID \
  --expected-grant-version 1 --expires-at FIXED_FUTURE_EXPIRY --out /private/runtime/browser-token
```

Stdout contains only nonsecret grant, result, version, expiry and destination metadata. Existing files,
symlinks and stdout destinations are refused. The one-time secret is never recovered by listing or
retrying. A backend normally exchanges `POST /api/v1/app-view-grants/{grant_id}/tokens` directly,
with `project_id`, `result_id`, positive decimal-string `expected_grant_version` and required
`expires_at`. There is deliberately no MCP token-read action and no human confirmation on each mint.

Expiry must be future, at most five minutes away, and within both the grant's and the parent's current expiry.
Tokens are opaque, hash-only, nonrefreshable and audience `app_read`. A lost mint response requires
another bounded mint; no refresh credential exists. Renew only through your authorized backend.

## Browser request and refusals

Keep the token only in JavaScript memory. Send `Authorization: AppRead <temporary-token>` to
`GET /api/v1/app-views/{grant_id}/results/{result_id}` with `credentials: 'omit'` and no redirects.
The browser supplies its exact `Origin`. No token belongs in a URL, source, DOM, source map, local or
session storage, cookie, log, screenshot or retained network evidence.

Preflight admits only GET and the `authorization`, `accept`, `if-none-match` headers. The response
allows only the exact approved origin, never ambient cookies or wildcard origins. It exposes the
documented validator, retry, rate and budget headers. Every conditional read rechecks access and result
lifecycle **before** returning `304`; revoked, expired or erased authority never becomes a cached success.

Render coverage and value states as data. Withheld, unavailable, pending, expired, erased or invalidated
results are never measured zero. `401` ends this token's access; `403` refuses the requested
origin, resource and projection. Bounded `429` and temporary `503` use their stated `Retry-After`; stop
after a bounded deadline. `501` means the protocol is not configured; `502` means an inconsistent
response. Neither is evidence that the aggregate was erased. Hide old numbers on access and lifecycle
refusal. A projection has a 1 MiB bound, never a silently truncated answer.

## Frozen measurements and replacement

Refreshing grant G1 for result R1 and snapshot S1 preserves that saved measurement after new source data
arrives, or truthfully refuses or withholds it under current lifecycle and privacy. It creates no execution.
To show a changed measurement, explicitly execute R2 using separately authorized analytics
credentials, review a new grant and token for R2, select that result in your app, then revoke G1. Keep
result, snapshot and content-digest and execution-row evidence. Never widen G1 or execute on browser refresh.

Ordinary proof links still require the recipient's own Anectico access. A browser token grants none
and is never put into a proof URL. Use only a platform-declared result proof link.

## Stop access and understand theft

**Agents and keys** lists every authorized grant through continuation, with exact origins, resources,
projection, parent and project, version, expiry and status and observed last use. Missing use or unchecked
usage and budget information is explicitly unknown. This accounting identifies the grant and parent, not a
verified visitor. Review the server revoke preview and confirm it, or use:

```bash
anectico --project PROJECT_UUID app-view grant revoke --grant GRANT_UUID --expected-grant-version 1
anectico --project PROJECT_UUID app-view grant revoke --grant GRANT_UUID --expected-grant-version 1 --confirm-token SERVER_TOKEN
```

Revoke one grant to stop one share; revoke the parent to stop all its descendants. Policy denial,
parent scope removal and expiry, project and workspace deletion and result invalidation also stop future
reads. A freeze removes writes and is not a promise that all read access stopped.

A stolen token can read its one approved result and projection until expiry or a current refusal. Origin
checks constrain browsers; a nonbrowser thief can forge an Origin header. Revocation and erasure
cannot reclaim numbers already displayed, downloaded or copied. You own disposal of your app's
caches, logs, exports and external copies.

## What your agent hands back

The answer names the parent key's ID and name (never its secret), grant ID and version, result ID,
full projection selectors, every exact allowed origin, the token lifetime used (at most five minutes)
and the grant's expiry timestamp. It distinguishes sharing authority (`app_views:share` for the
authorized builder) from runtime mint authority (`app_views:mint` and required reads on that exact
parent key). Only the browser token is projection-restricted.

The answer explains expired and revoked access, that owning-result erasure invalidates the share,
and which coverage, usage or budget facts remain unknown. It describes the stolen token's limits:
the approved origin, result, projection and remaining lifetime, with no mint, execution or write
authority. A nonbrowser thief can forge Origin; copied numbers cannot be recalled.

Your agent reports actual local browser checks, control checks and independent value comparisons,
and labels any fixtures or untested states. If only the local app ran, it explicitly says that
provider or remote deployment and TLS were not run or qualified. A linked verification report
supports these facts but does not replace them in the answer.
