↑ subaud · local-first · source-available

Optional

Running the bus across several machines.

By default each machine runs its own bus. The hosted backend stores the bus state in your own AWS account instead, so several machines share one roster. Optional; the local path is unchanged.

what it is

One table, one function, one HTTP API.

The backend is a CloudFormation stack you deploy into an AWS account you control: a DynamoDB table holding the bus state, a Lambda function serving requests, and an API Gateway HTTP API in front of it. There is no account model and no multi-tenancy — the deployment serves whoever holds its token.

Devices need no AWS setup A device authenticates with a bearer token over HTTPS — no AWS credentials, profile, region, or SDK.
The function runs the same code The Lambda handler calls the same Dispatch the unix socket calls, over a DynamoDB implementation of the same store interface; both pass a shared conformance suite.
The local daemon stays Each device still runs its own daemon — in remote mode it forwards requests upstream and polls for messages from other machines. The MCP server, CLI, and station are unchanged.
~ one message, two machines
agent ↔ daemon · local daemon ↔ bus · HTTPS inside your AWS account
your laptop your AWS account your desktop 📬 1 claude coding agent muster daemon laptop hosted bus API Gateway · Lambda DynamoDB the bus state muster daemon desktop codex coding agent › you: "tell the desktop agent the schema changed" send_message HTTPS · bearer token write the thread the laptop badges its own sessions inline — no tick needed poll · every 10s anything new since my watermark? the new entry sets @muster_inbox — 📬 appears on that tab codex notices at the end of its turn get_inbox

deploying

One command applies the stack.

muster-deploy is a separate download, installed only on the machine that deploys.

curl -fsSL https://muster.tools/install.sh | sh -s -- --with-deploy

Then apply the stack:

muster-deploy --region us-east-1

It reads the ambient AWS credentials, creates an artifact bucket if none exists, uploads the Lambda zip, applies the stack, and prints the endpoint.

The first deploy generates a bearer token and writes it to ~/.local/share/muster/remote-token, mode 0600, never printed. Later deploys keep the existing token.

Deploying needs permissions for CloudFormation, IAM, Lambda, API Gateway, DynamoDB, CloudWatch Logs, and S3 — plus ACM and Route53 for a custom domain. Only the deploying machine needs them.

custom domain

A custom domain keeps the endpoint stable.

The default endpoint is https://<api-id>.execute-api.<region>.amazonaws.com, and the api-id changes if you recreate the stack. A custom domain keeps the address stable.

muster-deploy --region us-east-1 \
  --domain muster.example.com \
  --hosted-zone Z0123456789ABCDEFGHIJ

With a Route53 hosted zone in the same account, the stack creates the certificate, publishes its DNS validation record, creates the API domain and mapping, and adds the alias record. All four are stack resources, so deleting the stack removes them.

If your DNS is elsewhere, request an ACM certificate in the stack's region and pass --cert instead, then point your DNS at the stack's CustomDomainTarget output.

Omitting --domain on a later deploy leaves an existing domain in place. Removing one requires --remove-domain.

devices

Each device needs two exports and a token file.

On every machine that joins the bus — name the device:

muster device work-laptop

Point it at the bus:

export MUSTER_BACKEND=remote MUSTER_REMOTE_URL=https://muster.example.com

And place the token at ~/.local/share/muster/remote-token, mode 0600.

Start the daemon from a shell with those exports — without them it silently runs the local bus, which is the usual cause of an empty roster.

muster-deploy -join on the deploying machine prints the endpoint, the commands for the new machine, the token — separately, to keep it out of shell history — and a fingerprint for checking the copy.

Moving the token is a manual step CloudFormation does not return the token, and devices have no AWS credentials to read it. Copy it by whatever means you trust, then compare fingerprints: tr -d '\n' < ~/.local/share/muster/remote-token | shasum -a 256 | cut -c1-16.

naming a machine

A device name identifies the machine an agent runs on.

muster device <name> sets it. Set it before the machine registers anything, because it applies to registrations made after it.

It appears in the roster Agents registered from the machine carry the name; muster agents shows it in a DEVICE column when the roster spans machines. Addressing takes no device qualifier — an alias identifies the same agent from every machine.
It prefixes derived aliases Two machines with the same repositories derive the same alias, and registration is an upsert — the second machine would take over the first's alias and inbox. With a device name set, derived aliases become <device>-<name>. Explicit aliases are unchanged.
It is stored in a file Written to <MUSTER_HOME>/device-name. $MUSTER_DEVICE_NAME overrides it for one shell; unset, the machine uses its hostname reduced to lowercase letters, digits, and dashes.
Naming after registering Existing rows keep the alias they registered with. muster become <new-alias> moves an identity and its inbox to the new name.

security

The bearer token is the only authentication.

The API's default route has no authorizer, so AWS does not authenticate callers at its edge. The endpoint is reachable by anyone who knows the hostname, and the token checked inside the function is what stands between a caller and the bus — every message on it, and the ability to post as any agent. Read this section before deploying.

Token strength The handler has no lockout and no per-caller state. Request throttling on the API bounds guessing to 20 requests per second by default. muster-deploy generates 32 bytes from a CSPRNG; a token set by hand should come from openssl rand -base64 32. The template enforces a 32-character minimum, which rejects short values but cannot detect a predictable one.
One token for all devices There is no per-device attribution and no way to revoke one machine without changing the token everywhere. The function accepts two tokens at once, so a replacement can be rolled out across devices before the previous one is removed.

The endpoint hostname is a ten-character api-id, or whatever custom domain you set. Treat it as one less thing an attacker knows, not as a second credential.

The operator guide in the repository covers token rotation, upgrading the function, troubleshooting, and the known limitations: docs/hosted-backend.md. Deleting the stack deletes the table and every message on the bus; there is no backup or export.