> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bricks.tools/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace binding

> Pair a Buttress server with a BRICKS workspace and turn on JWT authentication

By default, a Buttress server runs in **public mode** — any client on the LAN can connect, no auth required. **Binding** the server pairs it with one BRICKS workspace and switches it into a mode where every WebSocket and file-transfer request must carry a valid workspace-scoped JWT.

| State   | Discovery announcement                        | WebSocket auth                     | HTTP file transfer                 |
| ------- | --------------------------------------------- | ---------------------------------- | ---------------------------------- |
| Unbound | `authentication.required = false`             | Open                               | Open                               |
| Bound   | `authentication.required = true` (with `kid`) | JWT verified, workspace must match | JWT verified, workspace must match |

Binding is a one-way pairing: a single Buttress server can only belong to one workspace at a time. Bound servers reject unauthenticated connections with WebSocket close code `1008`.

## Prerequisites

* The Buttress server is installed locally (see [Installation](/buttress/installation))
* [BRICKS CLI](/cli) is installed
* You are signed in to your workspace: `bricks auth login <passcode>`

## Bind a server

Run the binding command from the same machine that runs the Buttress server. The CLI auto-detects the local server id and writes a small `state.json` that the server reads at startup.

```bash theme={null}
bricks buttress bind
```

To override the auto-detected id, give the server a friendly name, or write the state file somewhere other than the default location:

```bash theme={null}
bricks buttress bind \
  --server-id buttress-mac-studio \
  --name "Studio LLM" \
  --state-dir /etc/buttress
```

For headless or remote setups, emit the state JSON to stdout and write it yourself:

```bash theme={null}
bricks buttress bind --print > /etc/buttress/state.json
```

<Warning>
  Restart the Buttress server after binding. The state file is read once at startup — without a restart, the server keeps running in public mode.
</Warning>

### What gets written

The state file lives at `~/.bricks-cli/buttress/state.json` by default. Override the directory with `--state-dir <path>`.

```json theme={null}
{
  "workspace": {
    "id": "<workspace id>",
    "name": "<workspace name>",
    "serverId": "<server id>",
    "issuerPublicKey": "-----BEGIN PUBLIC KEY-----\n…",
    "kid": "<key id>",
    "boundAt": "2026-05-08T12:00:00.000Z"
  },
  "serverKeyPair": {
    "publicKeySpki": "<base64 SPKI>",
    "privateKeyPkcs8": "<base64 PKCS8>",
    "kid": "<announce kid>"
  }
}
```

* `workspace.issuerPublicKey` is the Ed25519 public key for your workspace — the server uses it to verify every incoming access token.
* `serverKeyPair` is a per-server Ed25519 announce keypair generated locally on every `bind`. The public half is registered with the cloud (so launchers can verify signed UDP discovery packets) and the private half stays on disk; signed announcements are described in [LAN auto-discovery](/buttress/autodiscovery#signed-announcements).

<Warning>
  Treat `state.json` as a secret. The `serverKeyPair.privateKeyPkcs8` field lets anyone who reads it sign UDP announcements as this server. `bricks buttress bind --print` warns on stderr when it writes the JSON to stdout for the same reason.
</Warning>

Re-running `bricks buttress bind` mints a new announce keypair and rotates the registered public key. Any stale launcher cache pointing at the old key stops trusting the server until it picks up the new key from the cloud.

## Inspect bindings

`bricks buttress status` prints the local state file alongside the workspace-side list of bound servers.

```bash theme={null}
bricks buttress status
```

Add `--json` for machine-readable output:

```bash theme={null}
bricks buttress status --json
```

The JSON shape is `{ "local": <state.json or null>, "remote": <[{ serverId, name, boundAt, lastSeenAt }, …]> }`.

## Unbind

Removing the binding clears the workspace's record of the server **and** deletes the local state file.

```bash theme={null}
bricks buttress unbind
```

Keep the local state file (useful when you only want to revoke server-side):

```bash theme={null}
bricks buttress unbind --keep-local
```

After unbinding, restart the Buttress server to return it to public mode.

## Issue a long-lived access token

Foundation launchers obtain short-lived tokens automatically through the BRICKS cloud. For headless callers — CI scripts, CTOR agents, ACP bridges — mint a long-lived access token once and reuse it.

```bash theme={null}
# Default 30-day TTL
bricks buttress issue-token

# Custom TTL (seconds), JSON output for scripting
bricks buttress issue-token --ttl 3600 --json
```

The CLI prints the JWT and its expiry. Send it as either an `Authorization: Bearer <token>` header or as `?access_token=<token>` on the WebSocket URL.

<Tip>
  Issued tokens are tied to your CLI session token. Revoking the CLI's workspace token invalidates every Buttress access token issued from that session.
</Tip>

## Troubleshooting

| Symptom                                                       | Likely cause                                                               | Fix                                                              |
| ------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `Could not detect a local server id.`                         | Machine ID detection failed (sandboxed/container without identifying info) | Pass `--server-id <id>` explicitly                               |
| `Not logged in. Use bricks auth login first.`                 | No active CLI profile                                                      | Run `bricks auth login <passcode>`                               |
| Server still accepts unauthenticated connections after `bind` | Server was not restarted                                                   | Restart `bricks-buttress`                                        |
| `<serverId> was not bound to this workspace.` on `unbind`     | Server was already unbound, or bound to a different workspace              | Run `bricks buttress status` to see the actual binding           |
| Foundation devices show "no LAN provider is registered"       | Server is bound to a workspace different from the device                   | Re-bind the server to the device's workspace, or move the device |

## Next steps

<CardGroup cols={2}>
  <Card title="LAN auto-discovery" icon="broadcast-tower" href="/buttress/autodiscovery">
    How Foundation devices locate bound servers on the LAN.
  </Card>

  <Card title="Use from Foundation" icon="mobile" href="/foundation/buttress">
    Configure offloading on individual LLM/STT bricks.
  </Card>
</CardGroup>
