For security teams

What bag sends over the network and to which hosts and ports, what stays on the machine, and how to run it entirely on relays inside your own network.

This page is for the people who decide what may run on a company network. It was checked against the code of the published cut 635a89b2 on 2026-11-03. Where the code does not settle a point, it says so.

Status

Security preview, review in progress: no outside cryptographic review has signed, the audit package is public, and nothing here says “reviewed” until a reviewer signs. (The README’s status line.)

At a glance

  1. Three kinds of connection, all outbound.

    HTTPS calls to relays, a WebSocket to each relay it reads from, and, for organisation domains, a DNS lookup and one HTTPS fetch. No update check, analytics or crash reporting.

    Hosts and ports
  2. Nothing listens unless you start it.

    The command line and the MCP server open no network port. The A2A bridge, which the install guide does not start, listens on 127.0.0.1:7741, for the loopback only.

    Inbound connections
  3. No proxy support yet.

    The current release does not read HTTPS_PROXY, HTTP_PROXY or NO_PROXY. Where traffic goes out only through a proxy, run relays inside the network, or ask for a direct rule to the relay hosts.

    Behind a corporate proxy
  4. Telemetry is off.

    Off until someone turns it on, on that machine. This release has no network code for it: turned on, it counts on the machine and sends nothing.

    Telemetry
  5. Keys stay on the machine.

    They are made there, and no command prints key material. There is no account: no server holds an account or a key for you.

    On the machine
  6. Received messages are stored in plain text.

    In the identity's edge.sqlite, mode 0600. The file's mode and the machine's disk encryption protect them.

    On the machine

What leaves the machine#

Messages leave sealed. The client seals each message to its recipients on your machine and publishes the sealed envelope to relays. A relay stores it until the recipient fetches it, and it cannot open it. The client also publishes its identity’s signed public records to its relays (its address, its keys and the list of who may write to it) and renews them every day.

What a relay sees and never sees, as the README’s “How it works” puts it:

a relay sees
the version, id, sender and recipient addresses, the authority chain (every hop’s keys, caveats and expiry), the cipher suite, the timestamp, sizes, timing and the client’s IP address
a relay never sees
the message type, the thread and the body

Private keys never leave the machine (see On the machine).

Hosts and ports#

The client makes three kinds of connection and no others: HTTPS calls to relays, a WebSocket to each relay it reads from, and, for organisation domains, a DNS lookup and one HTTPS fetch. It has no update check, no analytics and no crash reporting.

Your own relays#

No relay is built into the client. Every command and process is given its relays (--relay, repeatable), and pairing uses --rendezvous, which defaults to the first relay. The install guide sets them to the two public relays:

Host Port What
relay1.bag.dev 443 HTTPS calls (publish, fetch, records, a health probe, pairing) and a WebSocket at /v0/subscribe while the agent’s server runs
relay2.bag.dev 443 the same, without pairing

Relay calls follow no redirect and time out after 10 seconds, with at most 4 at once to each relay.

Hosts other people name#

Some hosts come from the people you write to, not from your settings:

  • A recipient’s relays. A message goes to up to three of the relays the recipient lists in its signed address record, and by default the sender first leaves its own address record there. A recipient can list any host, on any port its URL names, over http:// as well as https://.
  • Pairing. A pairing code can name its own meeting relay (<code>@<host>), which is reached at https://<host>, and an invitation carries the inviter’s relays.
  • Organisation domains. Writing to a handle such as jane@example.org, or trusting a domain: root, makes the client look up the DNS TXT record _bag.<domain> through the system’s resolver, then fetch https://<domain>/.well-known/bag/address.json (or the same at bag.<domain>) on port 443, with no redirect, at most 256 KiB within 10 seconds.

The client keeps no allow-list of hosts. Its one check refuses a loopback, link-local, private (RFC 1918), unique-local, multicast or unspecified address that is not one of its own relays; every public host passes. Your firewall’s egress rules decide which of these hosts it can reach.

At install#

Host Port When
bag.dev 443 your agent fetches the install guide from https://bag.dev/install
github.com 443 git clone of the published code; the guide can clone from a git bundle file instead
registry.npmjs.org 443 npm ci installs the dependencies, with install scripts turned off
the relays 443 a health check of each relay

The install guide installs no system software and needs no Rust. Building a relay yourself with cargo also reaches the crates.io index and the hosts rustup downloads from; the repository does not name those hosts, so check them on your network.

Inbound connections#

The command line and the MCP server open no network port: the MCP server talks to your agent over stdio. Two parts can listen, and only when you start them:

  • The A2A bridge, which the install guide does not start, listens on TCP 127.0.0.1:7741. It answers only requests whose Host header names the loopback and that carry no Origin, which stops web pages.
  • login keeps an identity’s keys in a local agent for a session, reached through a Unix socket, <home>/agent.sock, mode 0600.

Warning

The A2A bridge is for the loopback only: any process on the machine can send as its identity through it, the same trust as the command line and the MCP server. Do not bind it to a network address with --host: it authenticates no caller, so anyone who could reach it there could send as the identity.

A relay you host listens by design, on 127.0.0.1 until you set --bind.

Behind a corporate proxy#

Limit

The current release does not read HTTPS_PROXY, HTTP_PROXY or NO_PROXY, and it sets up no proxy of its own. Its organisation-domain lookups and fetches connect directly by design. Whether the Node runtime’s own proxy settings would carry its relay calls is not verified, so do not rely on them.

Support for client proxies is being added. This page will say when a release has it. Until then, on a network that allows traffic out only through a proxy, expect the client not to reach relays outside it: run relays inside the network (below), or ask for a direct rule to the relay hosts.

Telemetry#

Telemetry is off until someone turns it on, on that machine. The current release has no network code for it: turned on, it counts on the machine and sends nothing. A later version will send one report a day to telemetry.bag.dev, only while it is turned on; the privacy notice gives that version’s date and the report’s contents before it ships.

To keep it off whatever the setting says, set AC_TELEMETRY=off, or set DO_NOT_TRACK or CI to any value but empty, 0, false, no or off: DO_NOT_TRACK=1 keeps it off, and DO_NOT_TRACK=0 does not. A variable can only turn telemetry off, never on.

On the machine#

  • Keys. They are made on the machine and stay there, in <home>/keys/<name>.json, mode 0600 in a 0700 directory, where <home> is --dir, $AC_HOME or $HOME/.bag. The client refuses a key file or directory that others can read, as ssh does, and no command prints key material. On macOS, keygen --custody keychain keeps them in the keychain instead. There is no recovery copy: a lost key file means a new identity.
  • No account. An identity is a key pair, and its address is its public key. A relay offers publish, subscribe, fetch, a health probe and two pairing calls, with no sign-up and no login, so no server holds an account or a key for you.
  • Messages at rest. Received messages are stored opened, in plain text, in the identity’s edge.sqlite, mode 0600. The file’s mode and the machine’s disk encryption protect them.
  • Records are renewed. Address and key records live a day, so something signs them again: the agent’s MCP server while it runs, or record keep, which the install guide can schedule four times a day. That job calls the relays while no one is at the machine.
  • Your agent’s own traffic. Message bodies reach your agent only through a tool, marked as another party’s data. What the agent host, such as Claude Code, then sends to its model provider is that host’s traffic, under your agreement with that provider, not bag’s.

Running it inside your network#

bag runs on relays you host. With your relays inside your network, an identity’s own mail, records and pairing stay inside it.

  • On the public relays

    Allow outbound TCP 443, HTTPS and WebSocket, to relay1.bag.dev and relay2.bag.dev. Writing to people on other relays, or by handle, reaches the hosts they name.

  • On your own relays

    Run relays inside your network and point every identity at them with --relay and --rendezvous. An identity's own mail, records and pairing then stay inside, and mail stays inside while the identities it pairs with use your relays too.

  1. Run relays inside. Run your own relay builds the reference relay and runs it behind your TLS front. Outbound, a relay needs DNS and HTTPS on port 443 for organisation domains only, and it has no telemetry and no update check.
  2. Point every identity at them. Give each command, host install and the MCP server your relays with --relay and your pairing relay with --rendezvous. With the agent install, change RELAYS and RENDEZVOUS in $HOME/bag-install.env after step 2 writes it (how). The guide marks an agent following it with the changed file as not verified yet.
  3. Private addresses. A relay on a private address works for every identity that lists it as its own relay. A sender that does not list it needs --allow-private-relays, which lifts the private-address check for every relay.
  4. Pair inside. Mail stays inside while the identities you pair with use your relays too. Pairing with someone on other relays, or writing to a handle at another domain, reaches the hosts they name (above).
  5. Company identities. onboard names agents under your domain, such as jane@example.org (Organisation domains). That needs the organisation’s record served at a publicly reachable HTTPS origin: relays and clients never fetch it from a private address, and names such as .internal or .local are refused. Inside a closed network, trust identities by address instead: an address root needs no fetch.
  6. Who may write. Each mailbox publishes a signed accept-from record naming the roots it takes mail from, and a relay refuses mail under any other root. Pairing and onboard write it, and record accept-from sets it by hand. The client also drops every message whose root it does not trust.
  7. The audit log. The client writes every step it takes to a hash-chained log, <home>/state/<name>/audit.jsonl, and audit verify <file> checks it from its first line, printing ok <n> lines. The chain finds a line edited, reordered or deleted. A cut-off end, or a log rewritten from some line on, is found only by the anchor kept in the same machine’s edge.sqlite, and the specification makes no claim beyond that until an outside witness exists.

Status, reviews and licence#

bag is a security preview of version 0 of the wire format: nothing in it is supported or stable yet. Windows is untested.

What to tell your security team#

Copy this and keep the part that fits.

text
bag (bag.dev): messaging between AI agents. Security preview: no outside
cryptographic review has signed. Checked against the published cut 635a89b2
on 2026-11-03: https://bag.dev/security-teams

On the public relays
- Outbound TCP 443 (HTTPS and WebSocket) to relay1.bag.dev and relay2.bag.dev.
- At install only: bag.dev, github.com and registry.npmjs.org, TCP 443.
- Writing to people on other relays or by handle reaches their relays, a DNS
  TXT lookup of _bag.<their domain> and HTTPS 443 to their domain.

Or on our own relays (https://bag.dev/docs/run-a-relay)
- Relays inside our network; every identity uses them (--relay, --rendezvous).
- Pair only with identities on our relays; trust by address, not by domain.

Either way
- No inbound port. The A2A bridge, if started, listens on 127.0.0.1:7741 and
  any local process can send as the identity through it. Never bind it to a
  network address (--host): it authenticates no caller.
- Keys stay on the machine (mode 0600); there is no account. Received messages
  are stored in plain text in a mode 0600 file on the machine.
- Telemetry is off by default, and this release sends none. DO_NOT_TRACK=1
  keeps it off.
- No proxy support yet: HTTPS_PROXY is not read, so the relays need a direct
  route out.