# Access Control

## What It Is

Access control determines who can chat with your public agent. By default, anyone with your public URL can start a conversation. You can restrict this so only invited people have access, and so the agent remembers each invitee separately without leaking what one visitor said to the others.

## Access Modes

Each public agent has one of two access modes:

| Mode | Who can chat | What the agent remembers |
|------|--------------|--------------------------|
| Open (default) | Anyone with the URL. No login required. | Public-scope knowledge only. Nothing per-visitor. |
| Gated | Invitation only. Visitors authenticate by clicking a fresh emailed link each session. | A separate per-visitor memory slice. Visitor A and visitor B never see each other's memory. |

## How to Set It Up

Tell {{productName}}: "Set my public agent to gated access" or "Make the coaching agent invitation-only."

{{productName}} flips the agent's access mode. The next visitor to your public URL sees a sign-in screen instead of the chat.

## Inviting Visitors

Tell {{productName}}: "Invite sarah@client.co to the coaching agent."

{{productName}} creates an invitation and emails the visitor a magic link. At creation time the invitation is stamped with a one-off `sliceToken` — that token is what binds every per-visitor memory write to this specific invitation for the life of the invite.

Only email invitations are supported. Phone, OTP, and password flows are not part of the current build.

## What Visitors Experience

- **First visit (invited):** The visitor opens the email and clicks the magic link. They land on your public URL, the cookie is set, and the chat opens. No password to remember.
- **Return visits / lost the email:** The visitor visits your URL directly, types the email they were invited on, and clicks "Send me a link." A fresh magic link arrives within seconds. The new link replaces the previous one — old links go inert.
- **Browser close:** The cookie is session-only. Closing the tab signs the visitor out. They click the latest magic link, or request a new one, to come back.
- **Revoked or expired:** Their next request is bounced back to the sign-in screen. They cannot get past it until you re-invite them.

## Per-Visitor Memory

Every gated visitor has their own ringfenced memory slice. When the agent talks to visitor A, it sees everything tagged with A's slice plus the agent's general public-scope knowledge. It cannot see visitor B's slice, and it cannot see your admin-scope notes. The same gate applies in reverse — nothing the visitor says leaks into your admin graph by accident.

The slice is populated automatically at the end of each conversation. When a visitor's chat session is reaped (idle timeout, or the visitor closes the tab), a background reviewer reads the transcript and writes anything worth saving into the visitor's slice. The visitor sees the new context the next time they return.

You can read what's in a visitor's slice via the cypher tools in conversation — "show me what we know about Sarah" — but the slice writes themselves happen autonomously without your involvement.

## Managing Access

All access management is done through conversation with {{productName}}:

- "Who has access to my coaching agent?" — lists active visitors and their `sliceToken`.
- "Revoke Sarah's access" — flips her grant to revoked AND immediately drops her active session, so she cannot continue talking on a live cookie. Her slice's historical memory stays in the graph; you can purge it separately if needed.
- "Extend Tom's access by 30 days" — pushes the expiry date forward. Slice unchanged.
- "Resend Sarah's invitation" — generates a fresh magic link and emails it. The slice stays the same, so her existing memory carries over.

Revoking + re-inviting the same person on a new invitation produces a fresh slice — the old slice's memory does not transfer. This is by design: a fresh invitation is a fresh relationship.

## Visitor Identity

When a visitor is authenticated, your public agent knows their name and contact details — it reads them from the visitor's `:Person` node, which is linked to their grant. It can personalise responses ("Welcome back, Sarah") without needing to ask.

## Action Approval

External-facing actions — sending emails, WhatsApp messages, Telegram messages, and erasing contacts — require your approval before {{productName}} executes them. This is human oversight as required by the EU AI Act.

When {{productName}} needs to send a message or perform a consequential action, it drafts the action and queues it for your review. You'll see it in your next chat turn:

- "Approve it" — {{productName}} executes the action immediately
- "Reject it" — the action is cancelled
- "Change the subject to X" — {{productName}} modifies the action and executes the edited version

Internal operations (creating tasks, updating contacts, searching memory) execute automatically without approval.

### Changing the Policy

Tell {{productName}} to change which actions require approval:

- "Auto-send follow-up emails from now on" — emails execute without approval
- "Require approval for all WhatsApp messages" — restores the default gating
- "What actions currently require my approval?" — lists the current policy

Changes are per-account and take effect immediately.

## Filesystem Access (SMB Share)

Brand isolation extends to the device filesystem. Every {{productName}} install provisions an SMB share scoped to that brand's install folder, credentialled by the brand's install owner and the {{productName}} PIN. A device that hosts more than one brand carries one share per brand; tearing one brand down never exposes another brand's files. See [Samba Share](./samba.md) for the credential model, per-OS mount syntax, and peer-brand lifecycle.
