# AccessGrants

> Who and what can act for you. Authorized apps and agents per workspace with their scopes and revoke, delegated agent keys, and a matrix of agents by resource with no access, read or read and write.

Source: https://docs.nasaqui.com/components/access-grants

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/access-grants.json
```

The other side of `OAuthConsent`: after you approve an app or connect an agent, this is where you see it, narrow
it and cut it off. Apps show their scopes; agents also get a row in a matrix where you decide, per resource,
whether they can read it or change it.

## When to use

- A "Connected apps" or "Agents and access" page of account or workspace settings.

## When not to use

- Signing in with Google or GitHub: use `ConnectedAccounts`.
- Your own API keys, not delegated to an agent: use `ApiKeys`.

## Import

```tsx
import { AccessGrants } from "@fadymondy/nasaq/web";
```

## Quick start

```tsx
<AccessGrants
  apps={apps}
  scopeLabels={{ "docs:read": "Read documents" }}
  organizations={orgs}
  onRevoke={(app) => api.revoke(app.id)}
  resources={[{ id: "docs", label: "Documents" }, { id: "billing", label: "Billing" }]}
  grants={grants}
  onChangeGrant={(agentId, resourceId, level) => api.setGrant(agentId, resourceId, level)}
  agentKeys={{ keys, scopes, onCreate, onRotate, onRevoke: revokeKey }}
/>
```

## Anatomy

```
AccessGrants             data-slot="access-grants"
├─ Alert                    outcome of the last action
├─ apps                     data-slot="access-apps": workspace filter, rows with scopes, dates, Revoke
├─ agent keys               ApiKeys, data-slot="access-agent-keys"
└─ matrix                   data-slot="access-matrix": agents by resources, a Select per cell
```

## API

| Prop | Type | Description |
| --- | --- | --- |
| `apps` | `ConnectedApp[]` | `{ id, name, kind: "app" \| "agent", publisher?, logo?, orgId?, scopes, authorizedAt, lastUsedAt? }`. |
| `scopeLabels` | `Record<string, string>` | Friendly scope names. |
| `organizations` | `{ id, name }[]` | Adds a workspace filter when there is more than one. |
| `onRevoke` | `(app) => Promise<void \| { error? }>` | Adds Revoke, with a confirm dialog. |
| `resources`, `grants`, `onChangeGrant` | | The matrix. `grants[agentId][resourceId]` is `"none" \| "read" \| "write"`. Optimistic, rolls back on failure. |
| `agentKeys` | subset of `ApiKeysProps` | Renders `ApiKeys` for delegated keys. Omit to hide. |
| `sections` | `("apps" \| "keys" \| "grants")[]` | |
| `labels` | `AccessGrantsLabels` | English and Arabic built in. |

## Accessibility

The matrix is a table; each cell select is named "agent: resource". Access is written out (No access, Read, Read
and write), never colour alone. Revoke asks first.

## RTL & i18n

Built-in English and Arabic. There are no brand logos: apps show the `logo` URL you pass, or their initials.

## Styling & tokens

Built on `SettingsSection`, `ApiKeys`, `Select` and `Badge`.

## Do / Don't

- Do enforce grants on the server for every agent call; this screen only edits them.
- Do not give an agent write access by default.

## Related

- [ApiKeys](https://docs.nasaqui.com/components/api-keys)
- [ConnectedAccounts](https://docs.nasaqui.com/components/connected-accounts)
- [OAuthConsent](https://docs.nasaqui.com/components/oauth-consent)

## Lab

https://docs.nasaqui.com/?path=/docs/components-security-access-grants--docs

## Code

### React

```tsx
<AccessGrants
  apps={apps}
  scopeLabels={{ "docs:read": "Read documents" }}
  organizations={orgs}
  onRevoke={(app) => api.revoke(app.id)}
  resources={[{ id: "docs", label: "Documents" }, { id: "billing", label: "Billing" }]}
  grants={grants}
  onChangeGrant={(agentId, resourceId, level) => api.setGrant(agentId, resourceId, level)}
  agentKeys={{ keys, scopes, onCreate, onRotate, onRevoke: revokeKey }}
/>
```

### shadcn

```tsx
<AccessGrants
  apps={apps}
  scopeLabels={{ "docs:read": "Read documents" }}
  organizations={orgs}
  onRevoke={(app) => api.revoke(app.id)}
  resources={[{ id: "docs", label: "Documents" }, { id: "billing", label: "Billing" }]}
  grants={grants}
  onChangeGrant={(agentId, resourceId, level) => api.setGrant(agentId, resourceId, level)}
  agentKeys={{ keys, scopes, onCreate, onRotate, onRevoke: revokeKey }}
/>
```

### Vue

```vue
<script setup lang="ts">
import { NqAccessGrants, type AccessLevel, type ConnectedApp, type GrantMatrix } from "@fadymondy/nasaq/vue";
import { ref } from "vue";

const apps = ref<ConnectedApp[]>([
  { id: "a1", name: "Notion Sync", kind: "app", publisher: "Acme Labs", orgId: "o1", scopes: ["docs:read", "users:read"], authorizedAt: "2026-08-01", lastUsedAt: "2026-09-28T10:00:00Z" },
  { id: "g1", name: "Support Agent", kind: "agent", publisher: "Nasaq", orgId: "o1", scopes: ["docs:read", "docs:write", "billing:read", "users:read", "users:write"], authorizedAt: "2026-09-01", lastUsedAt: null },
]);
const scopeLabels = { "docs:read": "Read documents", "docs:write": "Write documents", "billing:read": "Read billing", "users:read": "Read users", "users:write": "Write users" };
const organizations = [
  { id: "o1", name: "Acme" },
  { id: "o2", name: "Globex" },
];
const resources = [
  { id: "docs", label: "Documents" },
  { id: "billing", label: "Billing" },
];
const grants = ref<GrantMatrix>({ g1: { docs: "write", billing: "read" } });

async function onRevoke(app: ConnectedApp) {
  apps.value = apps.value.filter((a) => a.id !== app.id);
}
async function onChangeGrant(agent: string, resource: string, level: AccessLevel) {
  grants.value = { ...grants.value, [agent]: { ...grants.value[agent], [resource]: level } };
}
</script>

<template>
  <NqAccessGrants :apps="apps" :scope-labels="scopeLabels" :organizations="organizations" :on-revoke="onRevoke" :resources="resources" :grants="grants" :on-change-grant="onChangeGrant" :sections="['apps', 'grants']" />
</template>
```

### Blade

```blade
@php
    $apps = [
        ['id' => 'a1', 'name' => 'Notion Sync', 'kind' => 'app', 'publisher' => 'Acme Labs', 'orgId' => 'o1', 'scopes' => ['docs:read', 'users:read'], 'authorizedAt' => '2026-08-01', 'lastUsedAt' => '2026-09-28T10:00:00Z'],
        ['id' => 'g1', 'name' => 'Support Agent', 'kind' => 'agent', 'publisher' => 'Nasaq', 'orgId' => 'o1', 'scopes' => ['docs:read', 'docs:write', 'billing:read', 'users:read', 'users:write'], 'authorizedAt' => '2026-09-01', 'lastUsedAt' => null],
    ];
    $scopeLabels = ['docs:read' => 'Read documents', 'docs:write' => 'Write documents', 'billing:read' => 'Read billing', 'users:read' => 'Read users', 'users:write' => 'Write users'];
    $organizations = [['id' => 'o1', 'name' => 'Acme'], ['id' => 'o2', 'name' => 'Globex']];
    $resources = [['id' => 'docs', 'label' => 'Documents'], ['id' => 'billing', 'label' => 'Billing']];
    $grants = ['g1' => ['docs' => 'write', 'billing' => 'read']];
@endphp
<x-nq::access-grants :apps="$apps" :scope-labels="$scopeLabels" :organizations="$organizations" :resources="$resources" :grants="$grants" :sections="['apps', 'grants']" can-revoke can-change-grant />
```

### HTML + Alpine

```html
<div data-slot="access-grants" x-data="nqAccessGrants(JSON.parse('{\u0022locale\u0022:\u0022en\u0022,\u0022apps\u0022:{\u0022a1\u0022:{\u0022name\u0022:\u0022Notion Sync\u0022},\u0022g1\u0022:{\u0022name\u0022:\u0022Support Agent\u0022}},\u0022resources\u0022:[\u0022docs\u0022,\u0022billing\u0022],\u0022agents\u0022:[\u0022g1\u0022],\u0022cells\u0022:{\u0022g1:docs\u0022:\u0022write\u0022,\u0022g1:billing\u0022:\u0022read\u0022},\u0022labels\u0022:{\u0022summary\u0022:\u0022Reads {r}, writes {w}\u0022,\u0022revokeTitle\u0022:\u0022Revoke {name}?\u0022,\u0022revokeFailed\u0022:\u0022Access could not be revoked. Try again.\u0022,\u0022revoked\u0022:\u0022{name} was revoked.\u0022,\u0022grantFailed\u0022:\u0022That change did not save, so it was put back. Try again.\u0022}}'))" class="flex flex-col gap-6">
    <template x-if="notice !== null && notice.tone === 'success'">
        <div data-slot="alert" data-tone="success" role="status"
     x-data="nqAlert()" x-modelable="open" x-show="open"     x-on:nq:dismiss="notice = null" class="relative grid grid-cols-[auto_1fr_auto] items-start gap-x-3 rounded-card border p-3 text-start border-nq-success/30 bg-nq-success-soft">
    <svg aria-hidden="true" data-slot="alert-icon" class="mt-0.5 size-4 text-nq-success-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="12" cy="12" r="10"/>
  <path d="m9 12 2 2 4-4"/>
</svg>    <div data-slot="alert-body" class="flex min-w-0 flex-col gap-0.5">
                            <div data-slot="alert-description" class="text-body-sm text-foreground"><span x-text="notice.text"></span></div>
            </div>
            <div data-slot="alert-actions" class="ms-3 flex items-center gap-1">
                            <button data-slot="button"
     type="button"                         aria-label="Dismiss" x-on:click="dismiss()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 hover:bg-nq-hover size-control-sm p-0 text-muted-foreground [&_svg]:size-3.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M18 6 6 18"/>
  <path d="m6 6 12 12"/>
</svg></button>
                    </div>
    </div>
    </template>
    <template x-if="notice !== null && notice.tone === 'danger'">
        <div data-slot="alert" data-tone="danger" role="alert"
     x-data="nqAlert()" x-modelable="open" x-show="open"     x-on:nq:dismiss="notice = null" class="relative grid grid-cols-[auto_1fr_auto] items-start gap-x-3 rounded-card border p-3 text-start border-nq-danger/30 bg-nq-danger-soft">
    <svg aria-hidden="true" data-slot="alert-icon" class="mt-0.5 size-4 text-nq-danger-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="12" cy="12" r="10"/>
  <path d="m15 9-6 6"/>
  <path d="m9 9 6 6"/>
</svg>    <div data-slot="alert-body" class="flex min-w-0 flex-col gap-0.5">
                            <div data-slot="alert-description" class="text-body-sm text-foreground"><span x-text="notice.text"></span></div>
            </div>
            <div data-slot="alert-actions" class="ms-3 flex items-center gap-1">
                            <button data-slot="button"
     type="button"                         aria-label="Dismiss" x-on:click="dismiss()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 hover:bg-nq-hover size-control-sm p-0 text-muted-foreground [&_svg]:size-3.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M18 6 6 18"/>
  <path d="m6 6 12 12"/>
</svg></button>
                    </div>
    </div>
    </template>
            <div role="region" aria-labelledby="nq-settings-section-c889dbbc-title" data-slot="settings-section" data-tone="default"
    class="flex flex-col rounded-card border border-border bg-card py-4 text-card-foreground gap-5">
    <div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]"><h2 data-slot="card-title" id="nq-settings-section-c889dbbc-title" class="text-foreground text-h3">Authorized apps and agents</h2>
        <div data-slot="card-description" class="text-body-sm text-muted-foreground">Apps and AI agents you have allowed to act on your behalf. Revoking cuts their access at once.</div>
        <div data-slot="card-action" class="col-start-2 row-span-2 row-start-1 self-start justify-self-end"><div data-slot="select" x-data="nqSelect('all', false)" x-modelable="value" x-id="['nq-select']" x-model="org" class="contents">
    <button data-slot="select-trigger" x-ref="trigger" x-bind="trigger"
        aria-label="Workspace" class="flex h-control min-w-0 items-center justify-between gap-2 rounded-control border border-input bg-card px-3 text-body text-foreground min-h-[var(--nq-touch-min,0px)] cursor-default select-none outline-none transition-colors duration-150 ease-nq focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-popup-open:border-nq-focus data-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px] w-48">
    <span data-slot="select-value" x-text="label() ?? ''" :data-placeholder="empty() ? '' : undefined"
    class="min-w-0 flex-1 truncate text-start data-placeholder:text-muted-foreground"></span>
    <span class="flex shrink-0 text-muted-foreground [&_svg]:size-4"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m7 15 5 5 5-5"/>
  <path d="m7 9 5-5 5 5"/>
</svg></span>
</button>
                        <template x-teleport="body">
    <div data-slot="select-content" x-ref="popup" x-bind="popup" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 min-w-[var(--anchor-width)] max-h-[var(--available-height)] overflow-y-auto rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
        <div data-slot="select-item" x-bind="item('all', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('all')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">All workspaces</span>
</div>
                                                            <div data-slot="select-item" x-bind="item('o1', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('o1')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Acme</span>
</div>
                                                            <div data-slot="select-item" x-bind="item('o2', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('o2')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Globex</span>
</div>
    </div>
</template>
    </div></div></div>
    <div data-slot="card-content" class="px-4"><ul aria-label="Authorized apps and agents" data-slot="access-apps" class="flex flex-col divide-y divide-border rounded-card border border-border">
                                                                    <li data-org="o1" x-show="inOrg($el.dataset.org)" class="flex flex-wrap items-start gap-x-4 gap-y-2 px-4 py-3">
                            <span data-slot="avatar" x-data="nqAvatar(0)"
    class="inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-10 text-label rounded-control">
                    <span data-slot="avatar-fallback" class="flex size-full items-center justify-center" x-bind="fallback"
             role="img" aria-label="Notion Sync" >NS</span>
    </span>
                            <div class="flex min-w-0 flex-1 flex-col gap-1.5">
                                <div class="flex flex-wrap items-center gap-2">
                                    <span class="truncate text-label text-foreground">Notion Sync</span>
                                    <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border bg-secondary text-foreground"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M12 22v-5"/>
  <path d="M15 8V2"/>
  <path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"/>
  <path d="M9 8V2"/>
</svg>                                        App</span>
                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-nq-info/40 bg-nq-info-soft text-nq-info-text">Read only</span>
                                                                    </div>
                                <p class="text-caption text-muted-foreground">Acme Labs<span x-show="org === 'all'"> · Acme</span></p>
                                <div class="flex flex-wrap gap-1.5">
                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Read documents</span>
                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Read users</span>
                                                                                                        </div>
                                <p class="text-caption text-muted-foreground">
                                    Authorized <time data-slot="date-time" datetime="2026-08-01T00:00:00+00:00" dir="auto"     class="tabular-nums [unicode-bidi:isolate]">Aug 1, 2026</time>
 · Last used
                                    <time data-slot="date-time" datetime="2026-09-28T10:00:00+00:00" dir="auto"  title="Sep 28, 2026"     class="tabular-nums [unicode-bidi:isolate]">23 hours ago</time>
                                </p>
                            </div>
                                                            <button data-slot="button"
     type="button"                         data-app="a1" x-on:click="askRevoke($el.dataset.app)" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M10 11v6"/>
  <path d="M14 11v6"/>
  <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/>
  <path d="M3 6h18"/>
  <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>Revoke</button>
                                                    </li>
                                                                    <li data-org="o1" x-show="inOrg($el.dataset.org)" class="flex flex-wrap items-start gap-x-4 gap-y-2 px-4 py-3">
                            <span data-slot="avatar" x-data="nqAvatar(0)"
    class="inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-10 text-label rounded-control">
                    <span data-slot="avatar-fallback" class="flex size-full items-center justify-center" x-bind="fallback"
             role="img" aria-label="Support Agent" >SA</span>
    </span>
                            <div class="flex min-w-0 flex-1 flex-col gap-1.5">
                                <div class="flex flex-wrap items-center gap-2">
                                    <span class="truncate text-label text-foreground">Support Agent</span>
                                    <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-nq-accent/40 bg-nq-accent/15 text-nq-accent-text"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M12 8V4H8"/>
  <rect width="16" height="12" x="4" y="8" rx="2"/>
  <path d="M2 14h2"/>
  <path d="M20 14h2"/>
  <path d="M15 13v2"/>
  <path d="M9 13v2"/>
</svg>                                        Agent</span>
                                                                    </div>
                                <p class="text-caption text-muted-foreground">Nasaq<span x-show="org === 'all'"> · Acme</span></p>
                                <div class="flex flex-wrap gap-1.5">
                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Read documents</span>
                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Write documents</span>
                                                                            <span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">Read billing</span>
                                                                                                                <div data-slot="tooltip" x-data="nqTooltip(600, false)" x-modelable="open" x-id="['nq-tooltip']" class="contents">
    <span data-slot="tooltip-trigger" class="contents"><span data-slot="badge"     tabindex="0" class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-border text-muted-foreground">+2 more</span></span>
    <template x-teleport="body">
        <div data-slot="tooltip-content" x-bind="popup" x-nq-presence="open" x-anchor.top.offset.6="triggerEl"
            class="z-50 max-w-64 rounded-control bg-foreground px-2 py-1 text-caption text-background transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">Read users, Write users</div>
    </template>
</div>
                                                                    </div>
                                <p class="text-caption text-muted-foreground">
                                    Authorized <time data-slot="date-time" datetime="2026-09-01T00:00:00+00:00" dir="auto"     class="tabular-nums [unicode-bidi:isolate]">Sep 1, 2026</time>
 · Last used
                                    Never used                                </p>
                            </div>
                                                            <button data-slot="button"
     type="button"                         data-app="g1" x-on:click="askRevoke($el.dataset.app)" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M10 11v6"/>
  <path d="M14 11v6"/>
  <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/>
  <path d="M3 6h18"/>
  <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>Revoke</button>
                                                    </li>
                                    </ul></div>
    </div>
            <div role="region" aria-labelledby="nq-settings-section-9d276e36-title" data-slot="settings-section" data-tone="default"
    class="flex flex-col rounded-card border border-border bg-card py-4 text-card-foreground gap-5">
    <div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]"><h2 data-slot="card-title" id="nq-settings-section-9d276e36-title" class="text-foreground text-h3">What agents can reach</h2>
        <div data-slot="card-description" class="text-body-sm text-muted-foreground">Choose, per agent, which resources it can read and which it can change. Write includes read.</div></div>
    <div data-slot="card-content" class="px-4"><div class="overflow-x-auto">
                    <table data-slot="access-matrix" aria-label="Agent access by resource" class="w-full min-w-[32rem] border-collapse text-body-sm">
                        <thead>
                            <tr class="border-b border-border">
                                <th scope="col" class="py-2 pe-3 text-start text-caption font-medium text-muted-foreground">Agent</th>
                                                                    <th scope="col" class="px-2 py-2 text-start text-label text-foreground">Documents</th>
                                                                    <th scope="col" class="px-2 py-2 text-start text-label text-foreground">Billing</th>
                                                            </tr>
                        </thead>
                        <tbody>
                                                                                            <tr data-org="o1" x-show="inOrg($el.dataset.org)" class="border-b border-border last:border-b-0">
                                    <th scope="row" class="py-3 pe-3 text-start font-normal">
                                        <span class="block text-label text-foreground">Support Agent</span>
                                        <span class="block text-caption text-muted-foreground" data-agent="g1" x-text="summary($el.dataset.agent)">Reads 2, writes 1</span>
                                    </th>
                                                                                                                    <td class="px-2 py-3">
                                            <div class="contents" x-data="{ k: 'g1:docs' }">
                                                <div data-slot="select" x-data="nqSelect('write', false)" x-modelable="value" x-id="['nq-select']" x-model="cells[k]" class="contents">
    <button data-slot="select-trigger" x-ref="trigger" x-bind="trigger"
        aria-label="Support Agent: Documents" data-level="write" x-bind:data-level="cells[k]" class="flex w-full items-center justify-between gap-2 rounded-control border border-input bg-card px-3 text-body text-foreground min-h-[var(--nq-touch-min,0px)] cursor-default select-none outline-none transition-colors duration-150 ease-nq focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-popup-open:border-nq-focus data-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px] h-control-sm min-w-32">
    <span data-slot="select-value" x-text="label() ?? ''" :data-placeholder="empty() ? '' : undefined"
    class="min-w-0 flex-1 truncate text-start data-placeholder:text-muted-foreground"></span>
    <span class="flex shrink-0 text-muted-foreground [&_svg]:size-4"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m7 15 5 5 5-5"/>
  <path d="m7 9 5-5 5 5"/>
</svg></span>
</button>
                                                    <template x-teleport="body">
    <div data-slot="select-content" x-ref="popup" x-bind="popup" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 min-w-[var(--anchor-width)] max-h-[var(--available-height)] overflow-y-auto rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
        <div data-slot="select-item" x-bind="item('none', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('none')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">No access</span>
</div>
                                                                                                                    <div data-slot="select-item" x-bind="item('read', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('read')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Read</span>
</div>
                                                                                                                    <div data-slot="select-item" x-bind="item('write', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('write')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Read and write</span>
</div>
    </div>
</template>
    </div>
                                            </div>
                                        </td>
                                                                                                                    <td class="px-2 py-3">
                                            <div class="contents" x-data="{ k: 'g1:billing' }">
                                                <div data-slot="select" x-data="nqSelect('read', false)" x-modelable="value" x-id="['nq-select']" x-model="cells[k]" class="contents">
    <button data-slot="select-trigger" x-ref="trigger" x-bind="trigger"
        aria-label="Support Agent: Billing" data-level="read" x-bind:data-level="cells[k]" class="flex w-full items-center justify-between gap-2 rounded-control border border-input bg-card px-3 text-body text-foreground min-h-[var(--nq-touch-min,0px)] cursor-default select-none outline-none transition-colors duration-150 ease-nq focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-popup-open:border-nq-focus data-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px] h-control-sm min-w-32">
    <span data-slot="select-value" x-text="label() ?? ''" :data-placeholder="empty() ? '' : undefined"
    class="min-w-0 flex-1 truncate text-start data-placeholder:text-muted-foreground"></span>
    <span class="flex shrink-0 text-muted-foreground [&_svg]:size-4"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m7 15 5 5 5-5"/>
  <path d="m7 9 5-5 5 5"/>
</svg></span>
</button>
                                                    <template x-teleport="body">
    <div data-slot="select-content" x-ref="popup" x-bind="popup" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 min-w-[var(--anchor-width)] max-h-[var(--available-height)] overflow-y-auto rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
        <div data-slot="select-item" x-bind="item('none', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('none')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">No access</span>
</div>
                                                                                                                    <div data-slot="select-item" x-bind="item('read', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('read')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Read</span>
</div>
                                                                                                                    <div data-slot="select-item" x-bind="item('write', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('write')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Read and write</span>
</div>
    </div>
</template>
    </div>
                                            </div>
                                        </td>
                                                                    </tr>
                                                    </tbody>
                    </table>
                </div></div>
    </div>
            <div data-slot="alert-dialog" x-data="nqAlertDialog(false)" x-modelable="open" x-id="['nq-alert-dialog']" x-model="revokeOpen" class="contents">
    <template x-teleport="body">
    <div data-slot="alert-dialog-portal">
        <div data-slot="alert-dialog-backdrop" x-nq-presence="open" class="fixed inset-0 z-50 bg-nq-fg/15 dark:bg-nq-bg/60 transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0"></div>
        <div data-slot="alert-dialog-content" x-bind="popup" x-nq-presence="open" x-trap.noscroll="open"
            class="fixed inset-0 z-50 m-auto grid h-fit w-[calc(100%-2rem)] max-w-md gap-4 rounded-floating border border-border bg-popover p-6 text-popover-foreground outline-none max-h-[calc(100dvh-2rem)] overflow-y-auto transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
            <div data-slot="alert-dialog-header" class="flex flex-col gap-1.5 text-start"><h2 data-slot="alert-dialog-title" :id="$id('nq-alert-dialog', 'title')" class="text-h3 text-foreground"><span x-text="revokeTitle"></span></h2>
                    <p data-slot="alert-dialog-description" :id="$id('nq-alert-dialog', 'description')" class="text-body-sm text-muted-foreground">It loses access immediately and must ask you again to get it back. Its agent keys stop working.</p></div>
                <div data-slot="alert-dialog-footer" class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"><button data-slot="button"
     type="button"                         x-on:click="revokeOpen = false" x-bind:disabled="busy ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-control px-[var(--nq-control-pad)]">
        Cancel</button>
                    <button data-slot="button"
     type="button"                         x-on:click="runRevoke()" x-bind:aria-busy="busy ? &#039;true&#039; : null" x-bind:data-disabled="busy ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--nq-action)_88%,var(--nq-fg))] h-control px-[var(--nq-control-pad)]">
        <svg data-slot="spinner" aria-hidden="true" style="display: none;" x-show="busy" class="size-4 animate-spin motion-reduce:animate-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg>                        Revoke</button></div>
        </div>
    </div>
</template>
</div>
    </div>
```
