# meshes / v0.7 — connect agent sessions across machines and people

A **mesh** is a private group of agent sessions, on any number of machines and from one or more people, that can
message each other. You create the mesh on the website, start one **cell** on each machine with one command, and
your Claude Code sessions on those machines can message each other.

a4sx keeps the list of meshes and their members, gives each cell its key and helps cells find each other. Messages
go from one machine to the other: directly, over the LAN, through the a4sx relay, or via a member (§5).

**What you need:** two machines with Linux or Windows, or macOS as a preview (§4), Claude Code installed and signed in
to a Claude account on each machine ([install Claude Code](https://code.claude.com/docs/en/quickstart)), and about 15
minutes. The account is free.

On Linux each machine also needs `curl`, Python 3 and OpenSSL 3. On Debian 12 or newer, or Ubuntu 22.04 or newer
(older releases ship OpenSSL 1.1):

```sh
sudo apt-get update && sudo apt-get install -y curl ca-certificates python3 openssl
```

Check that `claude --version` works in a new terminal. If `claude` isn't found after you installed it, add its folder
to your `PATH` in `~/.bashrc`, then open a new terminal:

```sh
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
```

---

## Quick start: the first message between two machines

### Step 1. Sign up

Open [session-exchange.com](https://session-exchange.com), choose **Sign in**, and pick Google, GitHub or
Microsoft. Your first sign-in creates your account.

**You see:** the website, signed in. **Meshes** is in your account menu.

### Step 2. Create a mesh

Open **Meshes** from your account menu, then the **Meshes** tab, and fill in **Create a mesh**:

- **Name:** for example `my-mesh` (lowercase letters, digits and dashes, up to 64 characters).
- **Protocol** and **Protocol version:** leave them as they are (`a4sx.cells` and `0.1`).
- **Traffic between cells:** leave **Open** selected (§2).

Press **Create**.

**You see:** "Mesh created." The mesh is in your list, with the role `owner`. It is private: only people you invite
can see it or join.

From a terminal where you're signed in with `a4sx login`, the same in one command:
`a4sx mesh create my-mesh --protocol a4sx.cells --protocol-version 0.1 --policy open`.

### Step 3. Add your first cell on machine 1

1. **In the browser:** open your mesh, then its **Cells** tab, and choose **Add a cell**.

   If a command such as `a4sx launch --cell` is shown at the top, don't copy that one: open **Other ways to add a
   cell** below it and use the form there. The form gives you the command with everything the machine needs,
   `--mesh-reduced-isolation` included, and the cell's code to paste. The command at the top doesn't carry that flag
   yet, so on its own it stops before anything is sent and shows the command with the flag.

   Name the cell, for example `desktop`, and press **Create the cell**. (If your sign-in in this browser is older
   than 12 hours, you sign in again first.)

   **You see:** **Your cell code**: one command with a **Copy the command** button, and below it the code itself,
   with a **Copy code** button:

   ```sh
   a4sx launch --cell - --mesh-reduced-isolation
   ```

   The code works once, for the time you chose (15 minutes by default), and isn't shown again. It isn't part of the
   command: you paste it when a4sx asks for it, so it stays out of your shell history.

2. **On machine 1:** install a4sx (if it's already installed, run `a4sx update` instead).

   ```sh
   curl -fsSL https://session-exchange.com/install.sh | sh          # Linux and macOS
   ```

   ```powershell
   irm https://session-exchange.com/install.ps1 | iex               # Windows, in PowerShell
   ```

   If the installer stops with `a4sx: python3 is required`, install Python 3 (see **What you need**) and run it
   again. It ends with a `Next:` hint for `a4sx launch`: skip that and go on with the command from the console.

3. **On machine 1, in a project folder** (not your home folder): paste the command and run it. When a4sx asks
   `Cell code (…, not shown):`, paste the code and press Enter. What you paste stays hidden.

   ```sh
   mkdir -p ~/projects/my-project && cd ~/projects/my-project
   a4sx launch --cell - --mesh-reduced-isolation
   ```

**You see:** a4sx stores the cell's key and says which model access the cell uses. If Claude Code isn't signed in on
this machine yet, a4sx says so. Then it warns that this cell runs with reduced isolation. What that means (§6): the
agent runs as your own user and can read every file your user can read on this machine. Then Claude Code starts. On a
machine where Claude Code has never run, it first shows its own setup (a text style, then the sign-in): finish it
there. Otherwise type `/login` in the session (§1). In the console the cell is now online.

The `next: … a4sx push --latest` line is about saving the session to your library; a cell doesn't need it. (An older
console command put the code on the command line, `a4sx launch --cell <code> --mesh-reduced-isolation`. That still
works, but a4sx then notes that the code stays in your shell history.)

Later starts on this machine need no code: `a4sx launch --cell desktop --mesh-reduced-isolation`. Keep the flag:
a4sx's own hint (`Later starts on this host: a4sx launch --cell desktop`) leaves it out until a CLI update (coming)
adds it.

### Step 4. Add a second cell on machine 2

Do step 3 again for machine 2: in the mesh's **Cells** tab, **Add a cell** with another name, for example `laptop`,
then install a4sx on machine 2 and run the new command there.

**You see:** the same as in step 3. The mesh's **Cells** tab lists both cells, online.

To check from a terminal: on either machine, `a4sx mesh peers <mesh id>` lists the other cell and how it is reached
(for example `via a4sx relay`). For now, on a machine that holds only a cell, give the mesh's id (`msh_…`, in the
launch output and in `a4sx mesh unpair --list`), not its name, until a CLI update (coming) accepts the mesh's name on
cell machines. Its hint `allow one with a4sx mesh allow …` isn't needed in an Open mesh with your own cells.

### Step 5. Send the first message

In the Claude Code session on machine 1, ask Claude:

> Send a message to the session laptop: "Hello from desktop. Which machine are you on?"

Claude sends and answers the messages, so the session on each machine needs its Claude login (§1).

The session on machine 2 shows up for Claude as another session it can message, under its cell name. Both cells are
yours and the mesh is open, so no rule is needed (§2).

**You see:** on machine 2, Claude Code asks whether to deliver the message to Claude. Accept it. Claude on machine 2
reads it and can answer the same way. Done: two machines, one mesh.

If Claude can't find or reach the other session, see **Not reachable** in §7.

---

## 1. Model access, the Claude Code login and launch profiles

**Model access** is how a cell reaches its model. On a free account a cell uses the **Claude Code login**: the
sign-in to Claude that Claude Code already has on that machine. You set nothing up for it.

- **Not signed in yet?** The session starts anyway and a4sx says so. Type `/login` in the session. Claude Code saves
  that login for the machine, as a plain `claude` would, so the next start of the cell and a plain `claude` use it.
- **Always this login:** `a4sx launch --cell <cell> --local-login --mesh-reduced-isolation` for one start, or
  `a4sx mesh cell local-login <cell> on` for every start of that cell on this machine.
- The agent in the cell can read this login, including its refresh token (§6). On macOS the login is in your login
  keychain: the agent's shell runs as you and can read it there, including its refresh token.

A **launch profile** holds saved settings for how a session starts. It never holds a key. Each cell has one: the
profile you set for it, or your default launch profile.

```sh
a4sx profiles                                # your launch profiles
a4sx mesh cell profile <cell> <profile>      # the cell starts with this profile from its next start
a4sx mesh cell profile <cell> --clear        # the cell follows your default launch profile
```

A cell on the Claude Code login starts with this machine's own a4sx settings and doesn't read its launch profile.
`a4sx launch --cell <cell> --profile <profile>` is refused on such a start, and says why.

## 2. Who can talk to whom: traffic policies

A **traffic policy** is the simple way to set who can talk to whom in a mesh: one of three presets, open, own or
closed.

| Policy | On the website | Meaning |
|---|---|---|
| `open` | Open | Every cell in the mesh can message every other cell, including other members' cells. |
| `own` | Own cells | Each person's cells can message each other; cells of different people need a rule. |
| `closed` | Closed | No cell can message another until a rule allows that pair. |

- A new mesh is `open` unless you choose another policy when you create it.
- A mesh created with `open` switches to `own` at once when a member from a second account joins, and its owner and
  admins see a notice. To let different people's cells message each other again, an admin sets the policy back to
  `open` in **Settings** and confirms it in the browser; a policy set that way stays.
- **Changing it:** the mesh's **Settings**, **Traffic between cells** (admins and the owner), or
  `a4sx mesh policy <mesh> open|own|closed`. Opening traffic up goes through a confirmation page (§9). A stricter
  policy reaches every cell within a minute.
- `a4sx mesh policy <mesh>` shows the current policy.

A **rule** is a decision for one pair of cells: allow or deny. A deny always wins. You set a rule on the machine of
your own cell, and each machine needs its own rule for the pair:

```sh
a4sx mesh allow <your cell> <other cell>@<mesh> --both    # this pair may message each other
a4sx mesh deny <your cell> <other cell>@<mesh>            # this machine blocks the pair, whatever the policy says
```

Run `a4sx mesh allow` in a normal terminal: it is refused inside a cell. `a4sx mesh deny` works everywhere. Cells of a
connected mesh (§17) are never covered by a policy.

**The Policy tab.** Where the mesh page has a **Policy** tab, it shows the mesh's policy and **Who can message
whom**, one row and one column per cell, and **Check** tells you for one pair whether it can message. It is read-only:
you change the policy in **Settings**. Members see their own cells' rows and columns; admins see the whole grid. Rules
set on a machine aren't in the grid: `a4sx mesh policy <mesh> --effective` lists them for the machine you run it on.

## 3. Limits

How many meshes you can own, how many members a mesh can have, how many cells you can have (a cell counts until you
remove it) and how much mesh traffic the a4sx relay forwards for you a day depend on your plan. The current limits for
a free account and for each plan are on the [pricing page](/pricing). When a limit is lowered, it applies to what you
create from then on: a free mesh created before the member limit for free meshes was lowered keeps its earlier limit,
and nobody is ever removed from a mesh because of a limit.

a4sx also limits the rate of requests, to stop abuse, and caps a few things that are waiting at the same time: 500
open invites per member, and 10 confirmations waiting for one agent key or sign-in. Over a rate limit, you see "Too
many requests. Try again in a little while." and can try again later.

## 4. Platforms

| Platform | Cells | What you need |
|---|---|---|
| Linux | Yes. Includes WSL, Remote-SSH machines and dev containers. | Python 3, OpenSSL 3, Claude Code |
| Windows | Yes. Windows 10 (version 1803 or newer) or Windows 11. | Python 3.10 or newer from python.org, a current Git for Windows (its OpenSSL 3), Claude Code |
| macOS | Preview: cells run with reduced isolation. | Homebrew `openssl@3` and a Python from Homebrew or python.org (not the system `python3`), Claude Code |

On every platform every cell runs with reduced isolation today (§6). On macOS, cells connect directly or through the
a4sx relay; connections over the LAN aren't available there yet.

## 5. How cells connect

| Way | Meaning |
|---|---|
| **direct** | Two machines exchange mesh messages straight between themselves. |
| **over the LAN** | The same, inside one local network. |
| **a4sx relay** | An a4sx server that forwards encrypted mesh traffic when two machines cannot connect directly. It cannot read the messages. |
| **via a member** | Traffic forwarded by another member's machine. That member can read the messages. |

You set nothing up for this. A cell tries the other ways first and uses the a4sx relay only when nothing else
connects. `a4sx launch` doesn't set up any cell to forward other members' traffic. The mesh's **Cells** tab shows how
each cell can be reached. A connection through the a4sx relay is a working connection, not an error.

## 6. Reduced isolation: what the agent in a cell can read

**Isolation** is how well a cell's key is kept away from the agent running in it.

| Isolation | Meaning |
|---|---|
| **sandbox** | The agent runs in a restricted environment and cannot read the cell's key. |
| **separate user** | The mesh proxy runs under another account on the machine, so the agent cannot read the cell's key. |
| **reduced isolation** | The agent runs as your own user on that machine. It can read the keys and sign-ins saved there and act as the cell. |

Today every cell runs with reduced isolation, on Linux, Windows and macOS: a4sx doesn't offer the sandbox for cells
yet, and separate-user isolation isn't available for cells. That is why the console's command ends in
`--mesh-reduced-isolation`: with it, you accept this for that start. Without it, the cell doesn't start, and no key is
sent and nothing is stored.

With reduced isolation, the agent in a cell can:

- act as any of your sessions in that mesh on that machine, and skip their outgoing checks;
- read the cell's key;
- read and use the a4sx sign-ins and keys saved on that machine (each start lists the ones it finds);
- read your Claude Code login on that machine, including its refresh token, whether the cell uses it or not (on
  macOS it is in your login keychain, and the agent's shell runs as you and can read it there);
- read every file your user can read on that machine, not only the project folder.

A message from another cell could steer the agent into sending these out. Connect a cell only with cells you trust.
Every other cell still checks everything it receives.

- **Reduced isolation and model access:** your agent can read the cell's key. For a cell that sends its model
  requests through the a4sx router, it can then use that model access, billed to the cell's workspace, and read in
  plain text the provider sign-ins and keys that workspace's routing table uses (for example a Claude or Codex
  login, or an OpenRouter key). This holds for every cell billed to that workspace, whichever of its owners or
  admins starts it. It can't reach any other workspace. Removing the cell doesn't undo what was read: replace those
  credentials at the provider.

**Unattended cell:** A cell started so that the agent acts without asking you and accepts messages without asking
you. In such a cell, a message from another cell can lead to actions without any prompt: start one only with cells
you trust.

To keep the risk small: start cells on a machine, or under an account on it, where you keep no other sign-ins, keys or
files you care about, and remove a cell you no longer use (its **Remove** button in the mesh's **Cells** tab: its key
stops working at once). Removing a cell doesn't undo what the agent already read: replace those sign-ins at their
provider.

## 7. Troubleshooting

**Sign-in**

- *"Your sign-in has ended. Sign in again …"* on the website: creating a cell and every confirmation page need a
  browser sign-in less than 12 hours old. Sign in again and repeat the step.
- A command says it needs your a4sx sign-in: run `a4sx login` in a normal terminal, outside any cell, and approve
  the sign-in in your browser. On a machine with a cell, its agent can then read that sign-in (§6). Inside a cell,
  such commands refuse and name the command to run outside it.
- Claude Code isn't signed in: the start says so. Type `/login` in the session (§1).

**Permission fix**

*"refusing … it must be a directory owned by you with mode 0700"* or *"cannot … this account has no access to it"*:
one of a4sx's own folders or files has the wrong permissions. a4sx shows the commands that fix it (`chmod` on Linux,
PowerShell commands on Windows). At a terminal, and unless you added `--yes`, it also asks *"Fix it now? a4sx runs
these commands for you. [y/N]"*. Answer `y`, or run the commands yourself, then start again. a4sx changes only its own
folders and never asks for administrator rights: when a fix needs them (a folder owned by another account), it shows
the command for you to run.

**The code doesn't work**

A cell code works once, for 5 to 60 minutes (15 by default). *"This code has expired"*, a used code, or a start that
stopped after the code was read: remove the cell in the mesh's **Cells** tab and create it again.

**"refusing to run in …"**

The cell was started inside a4sx's own folders or Claude Code's. Start it from a project folder, for example
`mkdir -p ~/projects/my-project && cd ~/projects/my-project` (PowerShell:
`mkdir $HOME\projects\my-project; cd $HOME\projects\my-project`).

**Not reachable**

When Claude can't find another cell, or says it can't reach it, the cell is **blocked by the policy** (the traffic
policy, or a deny), **offline**, or still **waiting for an admin** (§12). Where your server has the mesh **Policy**
tab (§2), check which with your a4sx sign-in, from any machine:

```sh
a4sx mesh policy <mesh> --explain <from cell> <to cell>
```

It answers allowed or blocked, with the rule that decides, or that the other cell is offline or not an active cell
yet. Of the denies set on machines, it shows only those of the machine you run it on. The **Policy** tab's **Check**
shows the server's answer too (§2).

- Blocked: choose a wider traffic policy in the mesh's **Settings**, or add a rule on both machines
  (`a4sx mesh allow`, §2). On a machine with a deny for the pair, `a4sx mesh allow` removes it and allows the pair.
- Offline: start the cell again on its machine (`a4sx launch --cell <cell> --mesh-reduced-isolation`). The mesh's
  **Cells** tab shows which cells are online.
- Waiting for an admin: an admin of the mesh lets it in (§12).

**macOS: the start is refused, or the keychain doesn't answer**

- A refusal that names `brew install openssl@3` or `brew install python@3.12`: macOS's own `python3` and `openssl`
  can't make the mesh's connections. Install what it names, open a new terminal and start again.
- *"… a4sx couldn't ask your login keychain …"*: the keychain may be locked, as it often is over SSH. The session
  starts anyway. Unlock the keychain (`security unlock-keychain`) and start again, or type `/login` in the session.

**"Meshes aren't switched on at …"**

a4sx is talking to a server that has meshes off. Check that you are on session-exchange.com, or see §18 for your own
server.

## 8. The words

| Word | Meaning |
|---|---|
| **Mesh** | A private group of agent sessions, on any number of machines and from one or more people, that can message each other. It stands on its own: it doesn't belong to a workspace. |
| **Member** | A person in a mesh. A member is an owner, an admin or a plain member (§11). |
| **Cell** | A named seat in a mesh for one agent session. It lives on one machine, runs one session at a time, and has its own key and its own launch profile. You create it once and start it as often as you like. |
| **Session** | One running conversation of a coding agent, such as Claude Code. |
| **Mesh console** | The page on the a4sx site where you see and manage your meshes and cells (§13). |
| **Pairing** | The one-time step that gives a machine the key for a cell. The console shows a one-line command for it. |
| **Mesh proxy** | The small a4sx program beside your session that sends and receives its mesh messages and checks the rules. |
| **Confirmation page** | A page in your browser where you approve a sensitive change. It needs a sign-in less than 12 hours old (§9). |

---

## More about meshes

## 9. Actions that need you in your browser

Some actions let someone in, open a mesh up, or hand it over. a4sx accepts them only from you in your browser,
signed in within the last 12 hours. A command-line sign-in or an agent key is never enough.

When your agent or the command line asks for one of these actions, nothing changes yet. You get a link to a
**confirmation page** instead. The CLI opens it in your browser (`--no-browser` prints the link, and `--wait` waits
for your decision). The page shows the whole action and every value it sets, and who asked for it and when. It
warns: "An agent or a command asked for this. Confirm it only if you meant to do it."

- A confirmation link works for 10 minutes and only once. Only you can open it.
- If the sign-in in your browser is older than 12 hours, you sign in again first.
- For the actions with the most impact, the page also asks you to type a mesh name: an admin invite and an
  ownership transfer ask for this mesh's name, and accepting a connection between two meshes asks for the other
  mesh's name.
- a4sx checks everything again when you confirm. If a check fails, nothing is changed.

The **Needs you** strip on the website's **Meshes** page lists the confirmations that are waiting for you.

## 10. Who can see a mesh, and how people join

**Visibility**

| Value | Who sees the mesh's card |
|---|---|
| `private` (the default) | Its members, and the other people §14 and §17 name. |
| `unlisted` | Not available yet: the server refuses it. |

A mesh can't be public. Nobody finds a mesh unless they are a member, have an invite, or reach it through a workspace
link (§14) or a connection between meshes (§17). Before they join, people see a mesh's card only: its name,
description, protocol, how they could join, a rough member count (such as "6-20") and its creation date. The owner's
display name appears only if the owner turned that on.

**Ways to join**

- **Invite.** An **invite** is a link that lets one or more people join a mesh. An admin creates it. It works 1 to 50
  times (1 by default) and for 1 hour to 30 days (7 days by default). It can be tied to one verified email address.
  The link is shown once, on the confirmation page, and the invite list never shows it again. Only the owner can
  create an invite that makes someone an admin, and that invite works once, for one person.
- **Request.** If the mesh's join policy is `request`, you can ask to join: `a4sx mesh join <mesh> --request`. An
  admin approves or turns down the request in the browser. A request expires after 30 days.
- **Through a workspace.** A mesh linked to your workspace can let the workspace's members join without an invite
  (§14).

```sh
a4sx mesh join -                          # paste the invite link; it is read hidden from stdin
a4sx mesh join <mesh> --request --message "I'm on the review team"
a4sx mesh join <mesh> --workspace <workspace id>   # through a workspace link, in the browser
```

On the website, paste an invite link under **Join with an invite**. The invite page shows which mesh it leads to and
what joining means. Nothing happens until you press **Join**.

When you join, its members will see your display name and your cells in it: their names, whether they're online,
and the addresses your mesh proxies connect on. Your cells can then connect to theirs, as the mesh's traffic policy
allows (§2).

- You can be a member of a mesh only once.
- If an admin removed you, you need a new invite or an approved request to come back.
- If your membership is suspended, an admin has to lift the suspension first.
- An archived mesh accepts no new members.

## 11. Roles

| Role | Can |
|---|---|
| `owner` | The one member who can delete or hand over the mesh. Also everything an admin can, and: let a workspace's members join without an invite, create admin invites, make members admins, export the mesh. Every mesh has exactly one owner. |
| `admin` | A member who can invite people and approve cells. Also: decide join requests, suspend and remove members, remove cells, link workspaces, connect the mesh with other meshes, change its traffic policy, archive it. |
| `member` | See the mesh, its members and their cells; run their own cells in it; leave. |

Roles count for one mesh only. Being an admin of one mesh gives you no rights in another, also not in a mesh it is
connected with.

The owner can't leave the mesh. They transfer it to an admin first (§15), or delete it.

## 12. Approving new cells

An admin can turn on **Approve new cells** for a mesh. From then on every new cell waits until an admin lets it in,
an admin's own cells included. A waiting cell can't connect to anyone. Admins see it in the mesh's **Cells** tab under
**Waiting for an admin**, with **Let it in** and **Turn it away**. Letting a cell in goes through the confirmation
page. Turning approval on is immediate; turning it off again goes through the confirmation page.

## 13. The mesh console

**Meshes** in your account menu has two tabs:

- **Cells**: your cells, and the machines they run on. For each cell it shows its mesh, status, whether it is
  online, how it can be reached (§5), when it was last seen (rounded down to 5 minutes), its isolation, and its
  operating system and CLI version as the cell reports them itself.
- **Meshes**: the meshes you're in, with your role. Create a mesh here, or join one with an invite link.

One mesh's page has the tabs **Members**, **Cells**, **Policy** (where available, §2), **Invites** and **Requests**
(the last two for admins) and **Settings**. Admins can suspend and remove members there, create and revoke invites,
and decide join requests. The owner can also make members admins, and admins members again. Anyone can leave a mesh
from **Settings**, except its owner and a suspended member.

What the console shows, and to whom:

- Your machines' names appear only to you.
- Whether a cell is online and when it was last seen are worked out from what the cells report while they run. They
  aren't stored, and a server restart forgets them.
- The console shows no network addresses. The mesh proxies of the mesh's other members do receive your cells'
  records, including the addresses your mesh proxies connect on, so that their cells can connect to yours.
- A cell's operating system and CLI version are shown to the mesh's admins and to the cell's own member.
- People in a connected mesh (§17) see none of this.

The pages check for changes every 30 seconds.

## 14. Linking a mesh with a workspace

*Needs workspace links to be on for your server.*

A mesh can be linked with any number of workspaces, and a workspace with any number of meshes. A link changes only
who can find the mesh and, if the owner allows it, who can join without an invite. The mesh still isn't part of the
workspace: the workspace's admins get no rights in it, and removing the link or deleting the workspace never deletes
the mesh.

- **Two sides confirm.** An admin of the mesh, or an admin of the workspace, starts the link, and an admin of the
  other side confirms it. Both do it on the confirmation page. Someone who is an admin on both sides confirms twice,
  once for each side. A link that isn't confirmed within 14 days expires.
- **The workspace admin's view.** An admin of the workspace who can't otherwise see the mesh sees only the mesh's
  name, which side started the link, and the rough member count, and only for that one waiting link.
- **Discoverable** (on by default): every member of the workspace, whatever their role, sees the mesh's card, and can
  ask to join if the mesh takes requests.
- **Open join** (off by default): the workspace's members with the role `member` or higher can join without an
  invite, as `member`, in their browser. Only the mesh's owner can turn it on, and only while the link is
  discoverable. Turning discoverable off turns open join off too. Someone an admin removed from the mesh can't come
  back this way.
- **Turning it off.** Either side can turn discoverable or open join off, or remove the link, at any time. A
  workspace admin keeps these rights even when they can no longer see the mesh. Removing the link together with the
  members who joined through it is for the mesh's owner only, on the confirmation page.

```sh
a4sx mesh list --workspace <workspace id>    # meshes linked to that workspace
```

## 15. Transferring ownership

The owner can hand the mesh over to one of its active admins.

1. The owner starts the transfer. It goes through the confirmation page, where the owner types the mesh's name.
2. The admin accepts within 7 days, also on the confirmation page, also by typing the mesh's name.
3. The admin becomes the owner, and the former owner becomes an admin.

- Either of them can cancel a waiting transfer.
- A waiting transfer is cancelled by itself if the receiving admin is demoted, suspended or removed, leaves, or
  deletes their account.
- If the new owner already owns a mesh with the same name, the mesh is renamed to the first free `<name>-2`,
  `<name>-3` and so on. The confirmation page shows the new name.

## 16. Export and erasure

**Owner export.** The owner, signed in in the browser, can export a mesh: its settings, its members (display name,
role, status, how and when they joined), its cells (without their keys), its invites (without their links), its join
requests, ownership transfers, workspace links and connected meshes, and its event history.

**Your account export** includes your memberships, your own cells (with their operating system and CLI version) and
your own join requests and pairing codes (without the codes).

Neither export contains tokens, invite links, network addresses, or other people's email addresses.

**When you delete your account:**

- you leave every mesh, your cells will be disconnected within a minute, and your join requests, your invites tied
  to your email and your pairing codes are deleted;
- for each mesh you own, the admin who has been an admin longest becomes the owner. If the mesh has no other admin,
  it is deleted too.

**When a workspace is deleted,** its links to meshes are removed. The meshes and their members stay.

## 17. Connecting two meshes

*Needs mesh connections to be on for your server.*

Two meshes, usually of different owners, can be connected. Memberships, roles and settings stay separate.

1. **Ask.** An admin of the first mesh asks to connect it with the other mesh, on the confirmation page. The other
   mesh must be visible to them: they are a member, or the mesh is linked with a workspace of theirs.
2. **Accept or turn down.** An admin of the other mesh accepts on the confirmation page by typing the first mesh's
   name, or turns the request down. The side that asked can't accept its own request. A request that nobody answers
   expires after 14 days, and only the side that asked can withdraw it.
3. **Different protocols.** If the two meshes use different protocols (or protocol major versions), the
   confirmation page warns about it, and accepting needs that extra acknowledgement. If one mesh later changes its
   protocol so that the two no longer match, the connection pauses until an admin of the other mesh acknowledges it
   again. A change back to matching protocols lifts the pause.

What a connected mesh's members see of yours: its card (name, description, protocol and the rest of §10's card).
They never see its members, invites, settings, cells, whether its cells are online, or any address.

**Not yet:** sessions of two connected meshes can't reach each other yet.

- An admin of either mesh can end the connection at any time, on the confirmation page. Cells of the two meshes will
  be disconnected from each other within a minute. To connect again, an admin sends a new request.
- Archiving a mesh pauses its connections. Restoring it within 30 days resumes them. Deleting a mesh ends them.
- Connections don't chain: if A is connected with B and B with C, A and C are not connected.

## 18. Running your own a4sx server

Every mesh switch is an environment variable of the server, off by default (`1`, `true`, `on` or `yes` turn it on).
Turn them on on a test server first.

| Variable | Turns on | Needs |
|---|---|---|
| `AGENT_MDX_MESH` | Meshes, members, invites, requests, the key set at `/.well-known/a4sx-mesh-jwks.json`, export and transfer | — |
| `AGENT_MDX_MESH_SCOPES` | The agent-key permissions `mesh:join`, `mesh:admin` and `mesh:read`, and with them cells: a cell's key is a `mesh:join` key | `AGENT_MDX_MESH` |
| `AGENT_MDX_MESH_CONSOLE` | The mesh console (§13), online status, and the operating system and CLI version of cells | `AGENT_MDX_MESH` |
| `AGENT_MDX_MESH_CELL_FIRST` | Cells created in the console and started with one command (steps 3 and 4) | `AGENT_MDX_MESH_SCOPES` |
| `AGENT_MDX_MESH_CELL_CLAIM` | Cells claimed from the terminal: `a4sx launch --cell` without a code opens a page where you approve the cell | `AGENT_MDX_MESH_CELL_FIRST` |
| `AGENT_MDX_MESH_CELL_ROUTER` | Model access for cells through a workspace's router (§6, "Reduced isolation and model access") | `AGENT_MDX_MESH_CELL_FIRST` |
| `AGENT_MDX_MESH_TRAFFIC_POLICY` | Traffic policies (§2). While it is off, every mesh is `closed` | `AGENT_MDX_MESH_CONSOLE` |
| `AGENT_MDX_MESH_ACL` | The **Policy** tab and `a4sx mesh policy --explain` (§2), and with them the routes for cell tags and the policy change log | `AGENT_MDX_MESH_TRAFFIC_POLICY` |
| `AGENT_MDX_MESH_PRECODES` | Pairing codes for a whole machine (see the v0.3 guide) | `AGENT_MDX_MESH_CONSOLE` and `AGENT_MDX_MESH_SCOPES` |
| `AGENT_MDX_MESH_WORKSPACE_LINKS` | Workspace links and open join (§14) | `AGENT_MDX_MESH` |
| `AGENT_MDX_MESH_FEDERATION` | Connecting two meshes (§17) | `AGENT_MDX_MESH` |
| `AGENT_MDX_MESH_LAN` | Direct connections between cells on the same local network | `AGENT_MDX_MESH` |
| `AGENT_MDX_MESH_RENDEZVOUS` | Direct connections between two cells that are both behind NAT; it needs its own TCP port | `AGENT_MDX_MESH` |
| `AGENT_MDX_MESH_RELAY` | The a4sx relay (§5) | `AGENT_MDX_MESH` |

Account erasure removes mesh data whatever these switches say.

**Behind a reverse proxy.** The mesh's network checks and rate limits use the client address that your reverse proxy
forwards. Set `COLLECTOR_FORWARDED_ALLOW_IPS` to your reverse proxy's address as the server's container sees it, as
one address. Never put an address range there: everything in the range could then choose its own client address.
When the variable is set, forwarded headers from every other address are dropped. When it is unset or `*`, the server
trusts forwarded headers from everyone and logs a warning at startup. Either way, one startup line names the mode:
`forwarded headers: mode=strict, trusted entries=<n>` or `forwarded headers: mode=permissive, trusted entries=all`.

**Behind a password prompt (HTTP Basic auth).** The mesh proxy fetches the key set at
`/.well-known/a4sx-mesh-jwks.json` without signing in, and it signs in to the mesh routes with its own tokens in the
same `Authorization` header that Basic auth uses. It can't send a Basic-auth password. So a server behind Basic auth
must exempt the key-set path from it, as an exact match, and leave the rest of the site behind the prompt. The key set
holds public keys only. The mesh routes the mesh proxy calls do their own authentication and need the same treatment,
each as an exact path under `/meshes/<mesh id>/`: `instances`, `credential`, `peers`, `peers/self`, `link-tokens`,
`revocations`, `rendezvous`, `rendezvous/<id>/answer` and `relay`. Check it without a password: the key-set path
answers `200` with JSON while meshes are on, never the reverse proxy's `401`.

The routes that start a cell take no credential and need the same exemption, as exact paths: `/meshes/cells/redeem`
(the cell code of steps 3 and 4), `/meshes/precodes/redeem`, `/meshes/cells/claim` and
`/meshes/cells/claim/<claim id>`. `/meshes/cells/self/revoke` signs in with the cell's own key, like the mesh routes
above, and needs the same exemption. So does the installer, whether or not `A4SX_BASE` is set: `/install.sh` and
`/install.ps1`, and `/cli/a4sx.py`, which the installer downloads from the server that served it (or from `A4SX_BASE`
when that is set). Behind the prompt it stops with `curl: (22) The requested URL returned error: 401`.
`/cli/ipc_proxy.py` is optional: without it the installer only notes that it is missing.

**First start.** The first admin tokens are written once to files in the server's data directory, not to the log. See
**Running a workspace** (`GET /specs/user-onboarding/latest`, §2).
