Savi Comms

For Savi Comms 2026.11.5 on one machine of your own:  a Mac, or a Linux machine with systemd.  The download carries the same steps in docs/QUICKSTART.md.  For a Linux server in Azure, reachable over the internet, use the server install guide.

Installing Savi Comms on one machine

For:  the administrator installing Savi Comms for their company on one machine:  a Mac, or a Linux machine or virtual machine with systemd, used from its own screen or from another computer over SSH.

To install on a Linux server in Microsoft Azure, reachable over the internet with Microsoft Teams connected, use docs/INSTALL-SERVER.md instead.  The two guides agree on everything they share.

What you are installing

Savi Comms runs on one machine you choose.  It gives your AI agents a mailbox each, wakes an agent when mail arrives, checks every action an agent takes against your rules, and sends the decisions that need a person to you, on a page called the Decision Desk.

Three things are worth knowing before you start.

What the machine needs

What Needed How to check
Operating system macOS, or Linux with systemd.  The Linux distributions tested are listed on savicomms.com, under Downloads, then Linux. Linux: systemctl --version prints a version
Python 3.12 or 3.13 on Linux; 3.12 on a Mac.  Each release carries compiled code built for exactly these, and an update refuses, before it changes anything, a Python it has no build for.  The container image uses 3.12. python3 --version
Disk encryption The disk that holds the store must be encrypted, or the broker refuses to start.  Linux:  LUKS (dm-crypt), including LVM on LUKS, or an Azure virtual machine's managed disks.  macOS:  FileVault, or an encrypted APFS volume. See "The encrypted disk" below
Claude Code Installed for the same user that runs Savi Comms.  No version is refused.  This release's console sign-in is tested with Claude Code 2.1.282 to 2.1.309; with any other version, a sign-in that fails names the version that ran. claude --version
Ports 8421 for the broker, on 127.0.0.1 only.  A chat port, 8431, opens only once a chat surface such as Microsoft Teams is set up.  Nothing to open in a firewall. SAVI_COMMS_PORT moves the broker's port. The health check in Step 3
Network Outbound https to Anthropic while agents work, and to the Savi Comms services to start a trial or use a license and to check it each day.  Nothing inbound.
Memory and disk Savi Comms states no minimum memory.  Two agent sessions run at once by default.  The server guide sizes its server at 2 processors and 8 GB of memory, and asks for 16 GB of free disk. free -h and df -h on Linux
Time About 30 minutes.

In the examples below, the company is Acme Robotics, the administrator is Dana Reyes, and the agents are Sentry (security), Orchid (ways of working) and Ada (a first working agent).  Use your own names.

The encrypted disk

Savi Comms checks, every time it starts, that the disk holding its store is encrypted.  If it cannot confirm it, the broker does not start, writes this to its log, and emails you once if this installation can send email:

Savi Comms will not start:  the disk holding its store (...) is not encrypted, or its encryption could not be confirmed (...).

When the check passes, the log reads disk encryption confirmed for the store.  The check looks at the store only, so choose where the store lives before Step 2.  Keep your agents' folders on an encrypted disk as well:  they hold your agents' work.

On Linux, the store's filesystem must sit on a dm-crypt device, which is what LUKS creates.  A whole-disk LUKS install, a separate LUKS data volume, and LVM on LUKS all pass.  An Azure virtual machine with managed disks also passes, because Azure encrypts those itself; the log then says the platform attested it rather than the machine.  Anything else is refused.  To check the folder you plan to use:

findmnt -no SOURCE --target ~
lsblk -s -o NAME,TYPE "$(findmnt -no SOURCE --target ~)"

How you know it will pass:  the second command lists a line whose TYPE is crypt.  If the store will live somewhere other than your home folder, name that folder in place of ~.

On a Mac, Savi Comms asks macOS whether FileVault is on for the volume that holds the store.  Two ways pass:

  1. FileVault on the startup disk.  Check with fdesetup status; it prints FileVault is On. Turn it on in System Settings, Privacy & Security, FileVault.  A Mac with FileVault off and the store on the startup disk is refused, Apple-silicon Macs included.

  2. An encrypted APFS volume of its own, with FileVault left as it is.  An encrypted APFS volume that is not the startup disk reports itself to macOS the same way, and passes.  To make one in Disk Utility:  choose View, then Show All Devices; select the APFS container that holds your startup disk; click the plus button above the list; name the volume, for example Savi Store; choose APFS (Encrypted) as the format and set a password.  Then use /Volumes/Savi Store/savi-comms-store as the store in every step below, and check it:

    diskutil info "/Volumes/Savi Store" | grep FileVault

    How you know it will pass:  the line ends in Yes.  The volume has to be unlocked and mounted for Savi Comms to start.  While it is locked the service cannot start; macOS keeps trying, and it starts once the volume is unlocked.

A Linux machine that restarts by itself

With the store on LUKS, the disk needs its passphrase every time the machine starts.  After a power cut nobody is there to type it, so Savi Comms cannot start until somebody does.  When the whole disk is on LUKS, the machine waits at the passphrase prompt.  When only the store's volume is, the system starts, the service keeps trying every five seconds, and Savi Comms starts by itself once the volume is unlocked and mounted.

If that wait is a problem, Linux offers ways to unlock without a person at the keyboard.  Each one trades some of the protection a typed passphrase gives.  Savi Comms checks only that the disk is encrypted, so all of these pass its check.  Which one fits is your decision, and keeping the typed passphrase is a valid choice.

Way How What it protects What it costs
Typed passphrase (what you have) Somebody types it at every start A stolen disk and a stolen machine stay locked Nothing starts after a power cut until a person types it
A key file on separate media A LUKS key slot opened by a file on a USB stick, named in /etc/crypttab A stolen disk without the stick stays locked Anyone holding the machine and the stick together can read everything.  An unattended start needs the stick attached, so in practice it stays with the machine
The machine's TPM2 chip systemd-cryptenroll --tpm2-device=auto on the LUKS device, and tpm2-device=auto in /etc/crypttab A disk pulled out of the machine stays locked A stolen machine, whole, unlocks itself when switched on; a firmware or boot-loader update can stop the automatic unlock until the passphrase is typed once and the key is enrolled again.  Adding --tpm2-with-pin keeps a PIN at every start, which brings back the wait
clevis with a tang server clevis luks bind against a tang server on your own network A disk or machine taken off your network stays locked The machine unlocks only while the tang server answers, so a power cut that also takes the tang server down still needs the passphrase; the unlock needs the network in the early boot stage

In every case keep the passphrase key slot, so the disk can still be opened by hand.

Step 1.  Unpack and install

Open a terminal:  on macOS, Terminal is in Applications, then Utilities.  Put the distribution file and its checksum file in your home folder, then check and unpack it, using the version number in the file's name:

cd ~
shasum -a 256 -c savi-comms-<version>.tar.gz.sha256
tar -xzf savi-comms-<version>.tar.gz
mv savi-comms-<version> savi-comms

On Linux, use sha256sum -c in place of shasum -a 256 -c.  The check prints OK; if it does not, do not use the file.

Check your Python first.  On Linux, python3 --version must print 3.12 or 3.13; on a Mac, 3.12.  These are the Pythons each release is built for:  its code is compiled, and compiled code runs only under the Python version that built it.  Ubuntu 24.04 ships 3.12 and Debian 13 ships 3.13, so python3 works as written there.  Debian 12 ships 3.11 and Ubuntu 22.04 ships 3.10, which no release is built for:  use a newer version of the system.  On a Mac, the python3 that comes with Apple's command line tools is older:  install Python 3.12 from python.org and name it, python3.12, in place of python3 on the last line below (or use the Savi Comms Mac app, which carries its own).  On Red Hat Enterprise Linux 9 or 10, Rocky 9, AlmaLinux 9 or 10, and Amazon Linux 2023, install the python3.12 package and name python3.12 the same way.

Then create the Python environment:

cd ~/savi-comms
SAVI_VENV=~/savi-comms-venv sh scripts/install_dependencies.sh install ~/savi-comms python3

This installs every package from requirements.lock.txt, which names the exact release of each package with its hash, using pip install --require-hashes.  A package that does not match its hash stops the install.  Packages that only one optional feature uses are left out unless you choose that feature:  the encrypted store is in by default, and a Postgres database or outgoing mail through an Azure Communication Services resource is added by putting SAVI_EXTRAS=postgres or SAVI_EXTRAS=email (or both, SAVI_EXTRAS=postgres,email) at the start of the same line.  The command says which features it installed and which it left out.

How you know it worked:  the last line reads install_dependencies: verified followed by a count of locked releases.

The Python environment, ~/savi-comms-venv, sits beside the program rather than inside it, so an update can replace the program's folder without rebuilding it.

Step 2.  Read the fixed rules, then create your installation

Savi Comms has a short set of rules that nobody at your company can change, including you.  They are what make it safe for agents to work without a person watching every step.  The installer shows them to you first.

A license or a free trial comes before the rules.  The package you download is encrypted, and it opens only for an installation holding one or the other.  The first time you run the installer it asks you, in the terminal:  type 1 to paste a license, 2 to start a free trial with a six-digit code sent to your email, or 3 for a page in a browser on this computer, which also offers signing in with Google or Microsoft.  One trial is allowed per company domain, and one per address at a free email provider.  This step needs the internet.  It also needs an available terminal for that question:  started by a tool with no terminal, the installer stops and says what it needs, and creates nothing.  The Savi Comms Mac app asks the same question in its own setup window, with a box to paste a license and a free trial started with an emailed code, and needs no terminal and no browser.

If your store goes on an encrypted volume of its own, use that path after --store here and in every later step.

Run the installer without accepting the rules:

cd ~/savi-comms
~/savi-comms-venv/bin/python installer.py --store ~/savi-comms-store --workspace-root ~/agent-workspaces \
    --human-name "Dana Reyes" --human-email dana@acme.example \
    --company-name "Acme Robotics" --company-short Acme \
    --security-agent sentry --air-agent orchid

It prints the rules and stops.  Nothing has been created except what holds your license or trial.

Read them.  When you accept them, run the same command again with --accept-fixed-rules added:

cd ~/savi-comms
~/savi-comms-venv/bin/python installer.py --store ~/savi-comms-store --workspace-root ~/agent-workspaces \
    --human-name "Dana Reyes" --human-email dana@acme.example \
    --company-name "Acme Robotics" --company-short Acme \
    --accept-fixed-rules --security-agent sentry --air-agent orchid

How you know it worked:  the last line reads:

bootstrap complete:  2 agents live, authority retired, audit chain verified=True

What the installer did:

If Claude Code is not installed yet, the installer ends with the steps to install it.  Follow them now:

curl -fsSL https://claude.ai/install.sh | bash
~/.local/bin/claude --version

How you know it worked:  it prints a version number followed by (Claude Code).  Do not run claude to sign in on this machine.  The installation signs itself in from its console in Step 5.  A sign-in typed in a terminal here is a second sign-in, and whichever of the two refreshes first retires the other.

Protect the root key.  Anyone who has both the store and the root key can read every secret in it, and without the root key nobody can, including you.  Keep it with your backups and nowhere else.

Step 3.  Start Savi Comms and check it

Start the broker by hand once to see it work:

cd ~/savi-comms
SAVI_COMMS_ROOT=~/savi-comms-store ~/savi-comms-venv/bin/python main.py

Its log includes disk encryption confirmed for the store.  If it stops with Savi Comms will not start: the disk holding its store, see "The encrypted disk" above.

In a second terminal, check its health:

SAVI_COMMS_ROOT=~/savi-comms-store sh -c '. ~/savi-comms/scripts/broker_address.sh && broker_curl -s "$(broker_url)/health"'

How you know it worked: "status":"ok" and "secrets_ok":true.

Why the check reads this way.  A new installation encrypts the connection to its broker, even on this computer, so the broker answers https on port 8421 with a certificate of its own, and a plain http:// address on that port gets no answer.  This line asks the program where its broker is and which certificate to trust, the same way the program's own scripts ask, so it works whether the connection is encrypted or not.  A new installation also keeps a plain way in for this computer only, on the next port up: curl -s http://127.0.0.1:8422/health.

Then check that both agents are ready to work:

SAVI_COMMS_ROOT=~/savi-comms-store sh -c '. ~/savi-comms/scripts/broker_address.sh && broker_curl -s "$(broker_url)/agents/sentry/liveness"'
SAVI_COMMS_ROOT=~/savi-comms-store sh -c '. ~/savi-comms/scripts/broker_address.sh && broker_curl -s "$(broker_url)/agents/orchid/liveness"'

How you know it worked:  each answers "live":true with an empty failures list.  If an agent is not live, the failures list says what is missing and what that breaks.

Stop the broker with Control-C before Step 4.

Step 4.  Run Savi Comms as a service

So Savi Comms keeps running after you close the terminal and starts again after a restart, install it as a service.

On macOS:

cd ~/savi-comms
scripts/install_macos_service.sh --company acme --store ~/savi-comms-store --workspaces ~/agent-workspaces

Use your own company's short name after --company, in lowercase.  The script finds ~/savi-comms-venv by itself, checks the service definition before loading it, and links the comms command into ~/.local/bin.  If that folder is not on your path, the script says so; add ~/.local/bin to your path in your shell profile.  Its logs go to ~/savi-comms-store/logs/.

On Linux:

cd ~/savi-comms
bash scripts/install_linux_service.sh --store ~/savi-comms-store --workspaces ~/agent-workspaces
loginctl enable-linger "$USER"

The script finds ~/savi-comms-venv by itself and links the comms command into ~/.local/bin.  Both commands need systemd; on a Linux system without it, the service script stops with systemctl not found.  The last command keeps Savi Comms running after you log out; if it refuses, run it with sudo in front.  To read its log, run journalctl --user -u savi-comms.

Who the agents run as.  Every agent session runs as the same account as the service:  on a Mac, the account that ran the script above, whose LaunchAgent the service is; on Linux, the account whose user service it is.  To the operating system an agent session is that account, so it can open the files that account can, including the ones the broker keeps to itself.  What stands between an agent and those files is its gate, which checks every action against your rules.  Woken agent sessions run without Claude Code permission prompts.  Every tool call is checked by the Savi Comms gate, which refuses by policy.  On a Linux server, agent sessions can run under an account of their own instead; docs/INSTALL-SERVER.md, Part 9, sets that up.

Which Claude sign-in they use.  Each session uses the sign-in you make in Step 5, which the installation keeps in its encrypted store.  Until you make one, it uses the Claude Code sign-in of the account the service runs as:  on a Mac, the item named Claude Code-credentials in that account's login Keychain; on Linux, ~/.claude/.credentials.json.  On a Mac, Claude Code itself may also keep a copy of a session's sign-in in that login Keychain, in items whose names start Claude Code-credentials-.

Where it runs from.  On both, the script copies the unpacked folder to ~/savi-comms-store/releases/<version>, points ~/savi-comms-store/releases/current at it, and the service starts that pointer.  An update unpacks the next version beside it and moves the pointer.  Leave ~/savi-comms and ~/savi-comms-venv where they are.

A machine whose service was installed from an earlier copy of this guide may still run ~/savi-comms and its .venv, so an update installs and does not take effect.  Run the service script once more from the newest version, with the same options and --venv naming the Python environment you already have.

How you know it worked:  the health check from Step 3 answers "status":"ok" with no terminal open.

Step 5.  Open the console and sign the installation in to Claude

The console is where you run the installation, and its Home page is where the installation signs in to Claude.

On the machine that runs Savi Comms, ask for a sign-in link:

~/.local/bin/comms console --print-only

It prints a link starting with https://localhost:8421/console?c= (http:// on an installation whose connections are not encrypted).  Open it in your browser within ten minutes.  It works once.  Until company sign-in is connected, the console then offers to set up a passkey, so you can sign in again without a link.

If you are working from another computer, use the two lines the same command prints after the link.  The first is an ssh -N -L line, which ends with your user name and this machine's name.  Run it on the other computer and leave it running; if the name at the end does not work, put in the address you reach this machine by.  The second is the link to open in that computer's browser.

On an installation that encrypts its connections, the tunnel carries the plain way in, port 8422, rather than 8421.  The installation's certificate is trusted on this machine only, so the encrypted link would show a certificate warning on the other computer.  The SSH tunnel encrypts the connection between the two computers by itself.

Then sign the installation in to Claude:

  1. On Home, find the Claude card and click Sign this installation in to Claude.  Click it once.
  2. Click Open the Claude sign-in page.  Sign in with your company's paid Claude account, and approve.
  3. Claude shows a code with two parts joined by a # sign.  Use its copy button so you get the whole code.
  4. Back on the console, paste it into The code Claude showed you and click Finish signing in.  Do this within ten minutes of step 1, and do not click the sign-in button again in between.

How you know it worked:  Home says This installation is signed in to Claude. A sign-in made this way lasts a year.  The same step offers An Anthropic API key instead, if your company pays for Claude that way.

Step 6.  Open the Decision Desk

The Decision Desk is where agents bring you the decisions only a person may make.

  1. Copy your sign-in token.  On macOS: pbcopy < ~/savi-comms-store/tokens/dana.token.  On Linux, run cat ~/savi-comms-store/tokens/dana.token and copy its one line.
  2. In your browser, go to https://127.0.0.1:8421/desk (http:// on an installation whose connections are not encrypted).
  3. The page asks for your token.  Click in the box, paste, and press Return.

How you know it worked:  the box disappears and you see your agents across the top.  The browser remembers the token, so you sign in once.

Step 7.  Add your first working agent

Your security and ways-of-working agents look after the installation.  Add the agents that do your company's work:

SAVI_COMMS_TOKEN=$(cat ~/savi-comms-store/tokens/dana.token) ~/.local/bin/comms seats add ada --folder "Ada - Operations Analyst"

How you know it worked:  the answer shows "live": true and an empty failures list.  Ada has a mailbox, her own token, her folder, your rules and her gate, and the broker did not need a restart.

The folder.  Every agent's folder sits directly under the workspace root you named in Step 2, ~/agent-workspaces.  Its name is the agent's name, then -, then its role: Ada - Operations Analyst makes an agent called Ada whose role is Operations Analyst, and the agent is told that role.  Choose the folder name with that in mind.

Savi Comms writes two things into each agent's folder. SAVI_COMMS.md is the agent's guide to the mail system, and it is written again whenever the agent is set up again. CLAUDE.md gets a block between the lines <!-- savi-comms --> and <!-- /savi-comms -->.  A folder with no CLAUDE.md gets a new one.  A folder that already has one keeps all of its own text, and the block is added at the end; setting the agent up again replaces only that block.

Your token goes on the same line as the command, and only for that command.  If you set it for the whole terminal instead, every later comms command in that terminal acts as you, including ones you run from an agent's folder.

Only you can add, remove or reassign an agent.  An agent that tries gets refused.

To see how many seats you are using, run SAVI_COMMS_TOKEN=$(cat ~/savi-comms-store/tokens/dana.token) ~/.local/bin/comms seats.  Your first two agents (security and ways of working) are listed as included and are not in the count.

Step 8.  Check that agents can reach each other

Use a separate terminal window for each agent.  A terminal remembers the first agent it acts as and refuses to act as a different one, so one agent cannot be made to act as another by changing folders.

Open a new terminal window for Sentry, and send a message from Sentry's folder:

cd "$HOME/agent-workspaces/Sentry - AI Security"
~/.local/bin/comms send --to ada --subject "Welcome to Acme" \
    --body "Ada, this is Sentry.  Confirm you can read your mailbox."

Open another new terminal window for Ada, and read her mailbox from her folder:

cd "$HOME/agent-workspaces/Ada - Operations Analyst"
~/.local/bin/comms inbox

How you know it worked:  the send answers "state": "delivered", and Ada's inbox lists the message.  Notice that you never told comms who was sending.  It knows from the folder it runs in, and an agent cannot send as anyone else.

This message does not wake Ada.  It is sent at normal priority and asks nothing of her, so the send also prints a line starting comms send: warning: that says it waits.  Reading her inbox by hand is how this step checks the mailbox without waiting.  "When your agents wake", below, gives the whole rule.

Step 9.  Check that a decision reaches you

When an agent needs something only you may approve, it files an ask on the Desk.  File one as Sentry, in Sentry's terminal window from Step 8, to see the whole path:

cd "$HOME/agent-workspaces/Sentry - AI Security"
~/.local/bin/comms desk-ask \
    --context "Ada joined Acme today as an operations analyst.  She needs read access to the shared reporting folder to build the weekly summary." \
    --question "Should Ada get read-only access to the reporting folder?" \
    --option "Grant read-only for 30 days | covers the first month, then reviewed" \
    --option "Grant read-only with no end date | less review work, wider standing access" \
    --option "Hold access | Ada drafts templates only" \
    --recommend 1

Refresh the Desk.  The ask is waiting, with the agent's recommendation marked.  Every ask gives you a last option, None of these are right, where you type what you want instead.

Click the option you choose.  The page offers you an optional note to send along with your decision; type one or leave it blank and confirm.  The ask leaves your queue, the agent receives your decision, and the decision is recorded in the Change Log.

When you approve access for a limited time, register it so it is tracked until it is removed.  Run this in Sentry's terminal window, since Sentry filed the ask and removes the access when the time is up.  The ask id is the "id" value comms desk-ask printed when Sentry filed the ask, and the broker refuses a grant whose ask you have not decided:

~/.local/bin/comms grant add --principal ada --scope "read-only, shared reporting folder" \
    --expiry 2026-10-16T17:00:00-05:00 --teardown-owner sentry \
    --verification "ada lists the folder and is refused" \
    --ask-id <the id comms desk-ask printed>

The Grants tab shows it until the access is removed.

Step 10.  Your agents' first real work

Agents wake on their own when mail asks them for something, using Claude Code on this machine and the sign-in from Step 5.

To confirm it, send an agent a message that asks for work, from Sentry's terminal window as in Step 8:

cd "$HOME/agent-workspaces/Sentry - AI Security"
~/.local/bin/comms send --to ada --asks-work --subject "First task" \
    --body "Ada, reply to Sentry with one line saying you are set up."

How you know it worked:  the send prints comms send: Wakes the recipient now. Then look in the broker's log for a line that starts with Waking and names that agent.  On macOS the log is in ~/savi-comms-store/logs/; on Linux, run journalctl --user -u savi-comms.  If the log instead mentions the Claude Code binary, Claude Code is missing or installed somewhere unusual; tell Savi Comms where with the SAVI_COMMS_CLAUDE_PATH setting.

When your agents wake

An agent costs nothing while it waits, and Savi Comms starts a session for it only when there is something to do.  Every message's sender sees which of these applied: comms send prints one line saying the message wakes the recipient now, waits for the digest, or is held, and why.

Mail.

A message is worth a session when it is urgent, needs a reply, asks for work, comes from you, or carries a Desk decision, or when the recipient's task file holds work that is working or queued.  A message that is none of these starts no session, at the digest either.  It stays unread, and the agent reads it at its next wake.

A message an agent flags --requires-human goes to you instead of waking anybody.  A message from an agent is also held, and sent to you instead, when its conversation has passed 10 hand-offs between agents or 12 messages, or has woken agents 16 times.  Your own messages are never held this way.  To reach the agent again, start a new conversation.

Your terminal.  While you work in an agent's folder from a terminal (see "Working as an agent from a terminal"), Savi Comms holds that agent's woken sessions until 5 minutes after the terminal was last used, so two sessions of one agent never work at once.  The installation setting SAVI_INTERACTIVE_WINDOW_SECONDS changes the 5 minutes, up to an hour.

Work an agent starts on its own.  By default agents start only when mail arrives.  In the console, Settings has a group called Working on their own:  a switch, Agents start work on their own; the days and hours; and Times a day agents may start on their own, across all agents, which is 0 when you install.  Set that number above 0, and during those days and hours (weekdays, 08:00 to 18:00, when you install) Savi Comms starts agents whose task files hold work, first the one the most other agents' work is waiting behind, at most 3 times a day for any one agent.

Those starts happen only while you are present.  Savi Comms counts you present for 90 minutes after you last used the console or the Decision Desk, ran comms as yourself, or used any agent's terminal.  If it cannot tell, it counts you present.  While you are away, the clocks that remind agents about unanswered mail, and escalate it to you, pause as well.  Mail still wakes agents while you are away, and so does a schedule you give an agent on its Configuration tab in the console.

How long a session runs, and how many at once.  A woken session on Claude is stopped after 30 minutes.  At most two sessions on Claude run at once, across the whole installation rather than per agent; a wake that finds both in use waits for one to finish.  Both are installation settings: SAVI_COMMS_WAKE_TIMEOUT, in seconds, and SAVI_COMMS_CLAUDE_SLOTS, each read when the broker starts, so restart it after changing one.  More slots finish work sooner and use your Claude allowance faster.  Each agent runs one woken session at a time, and a wake that arrives while it works joins that session.  The installation setting SAVI_WAKE_SESSION_CAPS, a JSON object naming an agent and its number, for example {"ada": 2}, lets that agent run up to 4 woken sessions at once, each still waiting for a slot like any other.  The agent's Configuration tab says which applies.  A session on a model of your own (see "Models of your own") is allowed 24 hours and waits for no slot.

Working as an agent from a terminal

You can do an agent's work yourself, from a terminal, in its folder. comms run inside an agent's folder acts as that agent:  it reads that agent's mail and sends as that agent, as Step 8 showed.  A terminal remembers the first agent it acts as and refuses to act as another, so use one terminal window for each agent.

While you work this way, Savi Comms holds that agent's woken sessions until 5 minutes after the terminal was last used, so you and a woken session never answer the same mail at once.  Mail that arrives meanwhile waits unread, and the agent is woken for it once the terminal has been quiet that long.

A Claude session as the agent.  Savi Comms places a launcher, savi-claude, in the store's bin folder each time the broker starts: ~/savi-comms-store/bin/savi-claude with the store this guide uses.  Put that folder on your PATH, or call the launcher by its full path:

~/savi-comms-store/bin/savi-claude ada

It starts Claude Code in that agent's folder, working as the agent.  The session gets the agent's connector secrets from the encrypted store, the same ones a woken session of that agent gets, so a ${NAME} in the folder's .mcp.json is filled in.  Any Claude Code option after the agent's name passes straight through, for example savi-claude ada --continue.  Before Claude Code opens, the launcher prints the names of the secrets it handed over, never their values, and which Claude sign-in the session runs on:

The launcher runs only as the account Savi Comms runs under.  Run as any other account, it names the account to use and starts nothing.

What the console shows of it.  Each agent's page in the console has a Terminal tab, for an administrator:  everything the agent's sessions said and did, oldest at the top, with earlier sessions above.  Each session is headed Woken session or Terminal session, with when it started and, while it runs, Running now.  A terminal session shows there when it runs as the account Savi Comms runs under, whether savi-claude started it or claude was started in the agent's folder.  The box at the bottom, Write to and the agent's name, sends the agent a message and wakes it; while a terminal is in use, the tab says the message waits until that terminal is quiet.  Stop ends a woken session.  A terminal session is stopped from its own terminal.

Work started in a terminal also shows where woken work does, for an administrator.  The agent's card on Home carries a line naming the last command, search or file the agent worked on, and the What it is running panel at the top of the agent's Dashboard tab lists its last fifty, newest first.  The agent's gate records each tool call it lets through, whichever session made it, so a terminal session's calls are on that line and in that list, and the panel reads Working now while the terminal is running them.

On a Linux server with workspace access, each person assigned to an agent works in its folder over SSH under their own account; docs/INSTALL-SERVER.md, Part 9.5, sets that up.

An agent that writes code

An agent you add cannot use git, and that is on purpose:  most agents never touch a repository.  When an agent's work is code, you turn that on for it in the console.

1.  Open the console, then Agents, and click Open beside the agent. 2.  Click the Configuration tab. 3.  In the Writing code card, tick This agent writes code.  The console asks you to confirm it is you, the same way it does for any change to an agent.

How you know it worked:  the card says the agent writes code now, and the agent's next git command runs.  Nothing needs restarting.

That one switch does three things together.  The agent may use git in its own repositories.  It is given the tier that developers hold, unless you had already given it a higher one.  And every push or deploy it makes waits for your yes on the Desk, so nothing it writes reaches a repository without you.

The card then shows the next step:  give the agent a GitHub App of its own, so its work on GitHub is recorded as its own and never as yours.  The guide that came with Savi Comms, docs/GITHUB-AGENT-IDENTITY.md, walks you through it, and the card shows the one command its last step runs, with the agent's name already filled in.

To undo it, untick the switch.  Git closes to the agent again, its tier goes back to what it was before, and it leaves the push gate.

Only you, the owner, can change this switch.  An agent that asks for git is asking you to make this change.

Closing a Desk decision with a commit.  When an agent's commit carries out a decision you made on the Desk, the agent writes Executes: <ask id> among the commit's trailer lines, and Savi Comms closes the ask once that commit is on the repository's main branch.  Savi Comms looks in every git repository directly inside ~/Developer, at origin/main as that folder last fetched it; it does not fetch for you.  If the repositories are somewhere else, name their folder with the installation setting SAVI_COMMS_CODE_DIR, or list them with SAVI_COMMS_WORK_REPOS, as a JSON list of paths.  An ask whose commit is not seen stays open.  comms desk-executed <ask id> records an ask carried out only when Savi Comms can see the work itself: that commit on origin/main, the ask's grant used or closed, a release naming it, or, for a step only a person can take, that person's own message.  A sentence or a commit number typed by an agent is not enough, so fetch the repository and the ask closes on its own.

An agent you let read a repository's history without writing code may run a short list of git commands that only read, such as git log and git status.  You choose that list under Settings, then Agents and models, then Reading code history.  Only commands that read are offered, so nothing on it can change a repository.

When the machine is offline

Savi Comms runs on the machine itself, so most of it carries on without the internet.  What changes:

Your own rules

Your agents start on the generic policy pack in packs/starter/.  To write your company's rules, copy that folder, put your rules into rules/*.md, list them in the pack's manifest, and install it:

cd ~/savi-comms
~/savi-comms-venv/bin/python -c "from services.policy_packs import install; print(install('packs/yours'))"

Every earlier version of every rule file is kept, archived by its hash.  To see which layer a rule lives in, and so who may change it, ask the broker:

SAVI_COMMS_ROOT=~/savi-comms-store sh -c '. ~/savi-comms/scripts/broker_address.sh && broker_curl -s "$(broker_url)/layers/status-vocabulary"'

Rules for one kind of agent

Some rules belong to a job rather than to the whole installation.  A pack whose manifest names applies_to_role lands on the agents carrying that role and on nobody else, and an agent then reads your installation's rules first and the role's rules after them:

~/savi-comms-venv/bin/python -c "from services.policy_packs import install_role_pack; print(install_role_pack('packs/savi-maker-developer'))"

A role pack places each of its files in one of two layers, and the manifest says which.  A file in the product, fixed layer ships with the product and cannot be changed here; an install or an edit that would change one is refused and tells you why.  A file in the customer, tunable layer is yours:

~/savi-comms-venv/bin/python -c "from services.policy_packs import edit_role_rule; print(edit_role_rule('savi-maker-developer', 'working-with-your-person.md', open('mine.md').read()))"

An agent picks up the pack at its next wake, and gives it back when it stops carrying the role.

Models of your own

Your agents run on Anthropic's models until you say otherwise.  An installation that would rather run models on a machine it owns adds that machine as a gateway, in Settings, under Models you bring yourself:

1.  What you call the machine, such as Studio box. 2.  Its address, such as http://192.168.1.40:11434. 3.  Which API it speaks.  Ollama, LM Studio and vLLM all speak the OpenAI API, and Savi Comms translates for them with nothing else installed.  An endpoint already speaking the Anthropic API is reached directly. 4.  The models it serves, such as qwen3:32b, gemma3:27b, qwen3:4b. 5.  A model for each of the three jobs:  hard work, the day to day work, and short jobs.  The same model in all three is fine when that is what the machine serves.  Every job needs one, because an agent whose short jobs have no model on your machine would send those back to Anthropic without telling you.

The machine is asked whether it is there as soon as you add it, and the answer is on the screen.  Its models then appear on every agent's screen beside Anthropic's, and an agent set to one of them runs entirely on your machine.

Teams, Slack and Google Chat

Savi Comms can carry your agents into Microsoft Teams, Slack or Google Chat, and each card for them is in the console under Settings, then Apps and connections.  All three deliver messages to an address on the public internet, over https, and an installation on one machine has none, since nothing on it listens on your network.  To use one of them, install on a server instead: docs/INSTALL-SERVER.md sets one up with a public address, then connects Teams in Part 6, and Slack and Google Chat in Part 6.3.

Running in a container

To run the broker in a container instead of on the machine, the build and run lines are at the top of the Dockerfile.  A container has no Claude Code sign-in of its own, so point SAVI_CLAUDE_CREDENTIALS_PATH at a mounted credentials file.  Your agents' model usage bills to your own Anthropic account, and Savi Comms never sees your credentials.  Model traffic goes from the session straight to Anthropic, except for an agent you put on a gateway of your own that speaks the OpenAI API:  that traffic passes through this installation's own bridge, on this machine, so that it can be translated.

Check the record

Everything in Steps 2 to 9 was written to a tamper-evident audit chain.  Check that it is intact at any time:

SAVI_COMMS_TOKEN=$(cat ~/savi-comms-store/tokens/dana.token) ~/.local/bin/comms admin verify

How you know it is intact: "ok": true.  The check finds an entry changed or removed anywhere inside the record.  It cannot tell whether entries were cut from the very end, or whether the whole record was rewritten from the start, which is one more reason to keep backups.

Backups

Back up two things together:  the store folder, ~/savi-comms-store, and the root key.  On macOS the root key is the Keychain item named savi-comms-root-key.  On Linux it is root.key inside the store.  A backup of the store without the root key cannot be opened.

The store from the command line

scripts/set_secret.py, in the folder you unpacked, lists, sets and deletes what the encrypted secret store holds from this machine's own command line, and its list prints names only, never values. scripts/encrypt_store.py encrypts the mail, the record and the index of a store made before stored data was encrypted by default:  run it with --dry-run first, then --apply with Savi Comms stopped, and --rollback puts the store back.

When something is wrong

What you see What it means What to do
Savi Comms will not start: the disk holding its store ... is not encrypted The store is on a disk Savi Comms cannot confirm is encrypted, or the encrypted volume is locked Unlock the volume, or move the store onto an encrypted one.  See "The encrypted disk"
The health check does not answer The broker is not running On macOS, rerun Step 4.  On Linux, run systemctl --user status savi-comms.  Without a service, start it by hand as in Step 3
"secrets_ok": false The secret store could not be read, most often because the root key is missing or comes from a different backup than the store Restore the root key from the same backup as the store
An agent is not live Its failures list names the missing piece Fix what the list names, then check liveness again
The console link does not open from another computer, or shows a certificate warning The SSH tunnel is not running, forwards a different port, or the encrypted link was opened instead of the one printed for another computer Run ~/.local/bin/comms console --print-only again, start the ssh -N -L line it prints, and open the link it prints for another computer
The console link has expired It lasts ten minutes and works once Run ~/.local/bin/comms console --print-only again
The Desk asks for your token again The browser lost it Repeat Step 6
The broker log mentions the Claude Code binary Claude Code is missing or not where Savi Comms looks Install Claude Code, or set SAVI_COMMS_CLAUDE_PATH
DENIED by the git-access gate The agent is not set up to write code, so git is closed to it If the agent's work is code, open it in the console and turn on This agent writes code (see "An agent that writes code").  Otherwise this is by design; the agent stops and asks you instead
An agent says it has no tier, or every action is refused Its tier was never set For an agent that writes code, turning on This agent writes code sets its tier.  See "An agent that writes code"
DENIED by the push gate Every push and deploy by an agent that writes code waits for your yes Answer the agent's ask on the Desk; the push then runs.  See "An agent that writes code"