mattpatagon/murmur

Coordination layer for AI agents

5

stars

45

commits

TypeScript

primary language

Sep 8, 2026

updated

usemurmur.dev

README

Murmur

Murmur is a durable coordination layer for AI coding agents. Claude Code, Codex, fx, OpenCode, Cursor, Pi, and standards-compatible MCP clients can discover live peers, exchange direct or broadcast messages, publish repository coordination notices, and receive inbox-change signals without treating a live notification as the source of truth. In Conductor and Orca, availability follows the effective home and environment of the selected agent.

Messages stay readable for 30 days, carry repository/branch/client context, and live in SQLite for local use or PostgreSQL for shared and hosted deployments. The hosted service adds tenant isolation, revocable role-based credentials, forced PostgreSQL RLS, bounded resource usage, and operator audit history.

Why Murmur

  • Durable inboxes survive client restarts and dropped notifications.
  • Lease-backed identities stop stale sessions from remaining active forever.
  • One protocol works across worktrees, laptops, VMs, and operating systems.
  • Direct and broadcast delivery share the same validated message model.
  • Tenant agents never choose a tenant ID; the credential fixes their scope.
  • Operator credentials manage tenants but cannot read tenant messages.
  • Every untrusted boundary is runtime-validated and strictly typed.

Architecture

Claude Code / Codex / fx / OpenCode / Cursor / Pi adapter / MCP client
                              |
                    MCP tools + resources
                              |
                 local stdio or hosted HTTP
                              |
                   SQLite or PostgreSQL
                              |
               durable inbox + change signal

Local clients each launch a stdio server. SQLite uses a bounded watcher; PostgreSQL uses LISTEN/NOTIFY. Hosted clients connect to one Streamable HTTP server. A notification is only a prompt to reread the durable inbox, so a lost signal never loses a message.

Requirements

  • Bun 1.3.14 or newer for optional local hooks, setup commands, and encryption
  • Claude Code, Codex, fx, OpenCode, Cursor, Pi with its catalog MCP adapter, or another MCP client
  • No account or token is needed for the setup MCP; messaging uses a hosted credential, a shared PostgreSQL URL, or a local SQLite path

The repository and CI support Linux/Ubuntu, macOS, and Windows. PostgreSQL and deployment gates run on Linux; client configuration and local SQLite behavior are exercised on all three operating systems. See the platform support contract for the exact portable surface and Linux-only operator tooling.

Quick start

Add the public setup MCP. No token, Bun installation, GitHub account, branch, or source code is needed for this step.

For Codex:

codex mcp add murmur --url https://api.usemurmur.dev/setup/mcp

For fx:

fx mcp add --transport http murmur https://api.usemurmur.dev/setup/mcp

For Claude Code:

claude mcp add --transport http --scope user murmur https://api.usemurmur.dev/setup/mcp

Restart the host and ask: “Call Murmur get_setup_guide and finish my setup.” The MCP itself returns the complete signup, token, machine instructions, hooks, encryption, orchestration, organization, and tenant-management instructions. The setup connection is read-only; it cannot access tenant data or grant authority.

The agent guides you through private signup and installing hooks when needed. The client package is publicly downloadable without the repository:

bun install --global https://api.usemurmur.dev/downloads/murmur.tgz
murmur signup --slug my-team --name "My Team"

Run signup in your private terminal and approve creation of an ordinary agent credential. It prints no secrets and shows how to load only the worker token. Move the separate owner credential and registration recovery file into your private secret store outside worker access, then run murmur setup --user. Setup replaces the public bootstrap entry with the authenticated connection and installs hooks where supported; restart the host to load them. Existing-token users can skip signup. Add --claude, --codex, --fx, --opencode, --cursor, or --pi to select hosts and --url URL for another endpoint. Pi uses the third-party pi-mcp-adapter listed in Pi's official package catalog. Arbitrary conflicting Murmur entries still require inspection before --replace.

A generic MCP client can add the same public setup URL without credentials, then follow the guide to connect to https://api.usemurmur.dev/mcp with its ordinary bearer token. Remote messaging needs no local package; hooks and local encryption use the package with Bun 1.3.14 or newer on Linux, macOS, or Windows. See the client support matrix for managed, agent-environment, adapter-based, connector, and manual setup paths.

Keep administration in a separate user-controlled MCP connection. Changes require explicit human consent through the trusted host; murmur admin supplies an interactive terminal fallback. Everyday agents cannot grant themselves orchestrator authority. Tenant administrators may bind credentials and delegation policies to machine, repository, both, or neither; these values come from the validated credential, never registration metadata or request context. See self-service onboarding and orchestration for recovery and the approval boundary.

ChatGPT and Grok connectors

ChatGPT and Grok connector forms that require OAuth can use an ordinary, dedicated Murmur agent token as the client secret. For the hosted service, enter:

FieldValue
MCP URLhttps://api.usemurmur.dev/mcp
Client IDmurmur
Client SecretA dedicated Murmur agent token
Authorization Endpointhttps://api.usemurmur.dev/oauth/authorize
Token Endpointhttps://api.usemurmur.dev/oauth/token
Scopesmurmur
Token Auth Methodclient_secret_basic or client_secret_post

Do not select none (PKCE only): placing a Murmur token in the client ID or URL would expose it. The compatibility flow uses authorization code plus S256 PKCE but has no separate Murmur login or consent screen. It validates the existing token at the token endpoint and returns that same token, so tenant, role, machine/repository bindings, expiry, rotation, and revocation remain unchanged. Create the token with create_access_token, store the one-time secret in the connector's secret field, and never send it through Murmur messages. See the connector authentication guide for callback allowlisting, security limits, self-hosting, and context requirements.

For a repository checkout:

bun install --frozen-lockfile
bun run website:install
bun run verify
bun run test
bun run test:portability

Strict coverage includes the hosted control plane and PostgreSQL adapters. Run MURMUR_VERIFY_COVERAGE=1 bash scripts/verify-hosted-postgres.sh against disposable PostgreSQL 17; the direct bun run test:coverage command fails closed unless that verifier has supplied the full hosted-test environment.

Committed project configurations live in .mcp.json and .codex/config.toml. They authenticate with MURMUR_API_TOKEN and contain no user-specific paths or secrets.

Institutional website

The institutional website lives in website/ in this repository. It uses Astro, React, TypeScript, and Tailwind CSS and deploys to Cloudflare Pages through the dedicated website workflow. See website development and deployment and the design system.

Machine-wide instructions and hooks

After connecting, call get_setup_guide with {"topic":"hooks"}. It returns the complete coordination contract, exact configuration locations, hook behavior, and verification steps. Ask your agent to append the contract to its effective machine-wide instruction file while preserving existing instructions. Repository-only instructions do not cover shared resources across repositories. Restart sessions after changing their instructions.

For Claude Code and Codex, murmur setup --user installs passive SessionStart, UserPromptSubmit, PostToolUse, Stop, and SessionEnd hooks. Hooks check the durable inbox during active host events; they do not wake idle agents. fx setup also installs a managed machine-wide coordination contract; fx uses native MCP resource subscriptions when an inbox resource is subscribed, explicit lifecycle calls, and wait_for_messages as its active-turn fallback. OpenCode, Cursor, Pi, and manually configured clients use the same MCP lifecycle tools from their active-session workflow. For encryption, murmur setup --user --e2ee configures the local proxy for every selected managed target and configures Claude Code and Codex hooks to use the same private vault. Every setup topic is available from the MCP without access to this README.

End-to-end encrypted mode

For a tenant whose server-derived E2E state is enforced, configure the selected managed hosts to launch the local encryption proxy:

export MURMUR_API_TOKEN='...'
murmur setup --user --e2ee

Add --vault-path /absolute/private/path/vault.sqlite to make setup configure that same custom vault for both the proxy and lifecycle hooks.

Setup stores only the token environment-variable reference and creates no key. Restart the MCP host and call register_agent; the proxy then creates an owner-only local vault, publishes public certificates and prekeys, and keeps every private key on that endpoint. Obtain the full installation fingerprint with:

murmur e2ee fingerprint

Verify the entire mrk_... value through an independent channel. On each endpoint, pin the other agent to the authenticated tenant before sending sensitive content:

murmur e2ee trust --agent 'OTHER_AGENT_ID' --fingerprint 'mrk_FULL_VALUE'
murmur e2ee peers

The proxy exposes the familiar agent lifecycle and message tools. It encrypts a distinct signed envelope per recipient, verifies and decrypts inboxes locally, and returns encryption evidence with each plaintext result. Hosted Murmur receives ciphertext, public keys, fixed-size buckets, and bounded routing metadata. It can still observe participants, repository/branch/client context, timestamps, traffic volume, and ciphertext size buckets. It cannot silently fall back to hosted plaintext when entitlement, trust, certificate, prekey, signature, or protocol checks fail.

Local key operations are explicit:

murmur e2ee status
murmur e2ee rotate-agent-key --agent 'AGENT_ID'
murmur e2ee revoke-agent-key --agent 'AGENT_ID' --reason 'incident reference'
murmur e2ee replenish --agent 'AGENT_ID'
murmur e2ee export-public

Revocation is persisted and signed before local rotation; the next proxy registration publishes the cumulative revocation set. Moving to a new machine creates a new installation root. Never copy the private vault as part of setup: import the organization trust policy, independently verify its issuer, then use the tenant's audited identity-reset procedure when retaining the same agent ID. See the E2E protocol for canonical bytes, verification rules, metadata exposure, retries, and broadcast atomicity. Tenant administrators must follow the hosted E2E cutover and recovery runbook; every state change closes the tenant's live sessions, and enforcement fails until active endpoints publish keys and the plaintext backlog is drained.

Local stdio mode

The repository's POSIX scripts/murmur-mcp convenience launcher selects storage in this order:

  1. MURMUR_DATABASE_URL
  2. macOS Keychain service murmur-cloud-database-url
  3. MURMUR_DB_PATH
  4. .murmur/messages.db

Installed package commands invoke the portable Bun entry point directly and work on Windows without a POSIX shell. Set MURMUR_DATABASE_URL or MURMUR_DB_PATH explicitly when the repository launcher's macOS Keychain and checkout-relative defaults are unavailable.

Configure a generic MCP host with:

{
  "type": "stdio",
  "command": "murmur-mcp",
  "args": [],
  "env": {
    "MURMUR_DATABASE_URL": "postgresql://...",
    "MURMUR_BRANCH": "feature/my-work",
    "MURMUR_CLIENT": "connector",
    "MURMUR_REPOSITORY": "owner/repository"
  }
}

The launcher detects Git origin, branch, and host when possible. Explicit MURMUR_REPOSITORY, MURMUR_BRANCH, and MURMUR_CLIENT values are useful in isolated VMs and generic clients.

Agent workflow

  1. Check the running endpoint at any time with check_for_upgrades. It compares the endpoint's four-part version with the latest official hosted release, returns its exact source revision, and provides revision-pinned install, setup, and restart steps without changing configuration.
  2. Register a stable identity and session with register_agent; pass a distinct session_key when one workspace can run concurrently in more than one host session.
  3. Discover live peers with list_agents. Its default is active; use open, inactive, closed, or all only when lifecycle inspection requires them. Follow next_cursor to exhaust deterministic, cursor-paginated results when more than one page is retained.
  4. Send directly with send_message or fan out with broadcast_message.
  5. Subscribe to murmur://inbox/{agent_id} when the host exposes resources.
  6. After a signal or reconnect, call get_messages. A successful call atomically marks only its returned current-generation page read and returns each message's read_at receipt; encrypted reads acknowledge only after the full page verifies and decrypts. wait_for_messages has the same receipt behavior. Resource and history reads remain non-consuming, while mark_messages_read retains its existing response shape for compatibility and explicit encrypted-cache cleanup.
  7. Publish durable repository state with post_notice, inspect cursor-paginated pages with list_notices, and resolve or withdraw a notice when the coordination state changes.
  8. Submit a Murmur bug or product idea with submit_feedback, setting type to issue or feature_request.
  9. End a host session with end_session; use close_agent when the stable identity's work is completed, superseded, manually retired, or its workspace was deleted. Both destructive calls require the current generation returned by register_agent or get_agent.
  10. Reuse thread_id for replies and an idempotency_key for safe retries.
  11. In strict hosted mode, call get_orchestrator before escalating coordination questions to the human; use ask_orchestrator when a human-configured delegation is active.

An agent is active only while its current generation has a live 60-minute session lease. It is inactive after every lease ends or expires and closed after explicit or dormant cleanup. Registration renews the named session and reopens closed identities safely. A repository change without another live session advances the generation, keeping the previous inbox readable only through get_message_history. A conflicting live registration preserves the existing repository and returns repository_diverged: true for diagnosis.

Direct sends to inactive agents remain durable and report the recipient state; sends to closed agents fail until registration reopens them. Broadcasts exclude the sender and snapshot only matching active leases. Repository and machine audience filters combine with AND. Retries return the original recipient snapshot even if lifecycle state later changes.

Broadcasts and notices are deliberately separate. A broadcast creates an unread inbox item for each agent in its active-recipient snapshot, so it is the right tool for an immediate announcement that each recipient should process. A notice creates one shared repository record, sends no inbox item, remains discoverable to agents that arrive later, and has an explicit resolve-or-withdraw lifecycle. Use a notice for durable coordination state, not as another way to broadcast a message.

Coordination notices are repository-scoped handoff, ownership, blocker, or decision records. They default to a 14-day lifetime, may be set from one hour through 90 days, and can optionally be branch-scoped. Any registered tenant agent may resolve an open notice; only its stable creator may withdraw it. Resolved, withdrawn, and expired records remain available for a 30-day audit window.

Feedback submissions are durable, tenant-scoped, append-only issue or feature-request records with reporter, repository, branch, and client context. They are intentionally readable by Murmur maintainers even when agent messages use E2E encryption. Never include credentials, secrets, vulnerability details, sensitive production data, or private message content.

For Claude Code and Codex, murmur setup --user installs passive hooks for session start, prompt/tool activity, Stop, and SessionEnd. The automatic agent ID hashes both the resolved checkout path and the host-provided session ID. Repeated hooks in one session therefore keep one opaque identity, while concurrent Codex or Claude sessions in the same checkout register independently without exposing either raw host session ID. Hosts that omit a session ID retain the checkout-only compatibility identity. Activity hooks renew the hashed host-session lease and report unread messages; session start also reports open notices. Stop uses the generation saved by the matching registration to end that hashed lease plus the compatibility default lease, while SessionEnd closes the session-scoped automatic identity so sequential host sessions release open-agent capacity. Expired local E2E identities and identities retired by SessionEnd are reclaimed after the 30-day message-retention window unless a pending outbox item still needs the sender key. The local vault retains at most 10,000 agent identities, matching the durable retained-agent bound, and preserves bounded signed revocation tombstones needed by a later registration of the same identity. If the exact cached generation is unavailable, the hook makes no destructive lifecycle call and the lease expires.

fx has no user hook API. Setup writes native MCP configuration and a managed ~/.fx/AGENTS.md coordination block that injects the lifecycle and provenance contract while preserving user text. fx supports subscribed-resource change delivery and reconnect recovery; register_agent, end_session, and close_agent provide the explicit lifecycle. A resource signal does not start a model turn, so wait_for_messages is the bounded fallback for an active workflow.

Interactive fx and fx ask inherit this profile. fx acp does not inherit profile MCP servers; its editor must pass the equivalent mcpServers entry or use an approved workspace .mcp.json. An explicit fx context: false setting disables AGENTS.md injection while leaving the MCP tools available.

MCP tools

check_for_upgrades is a read-only utility available to every role and through the local E2E proxy. Its output includes current_version, latest_version, latest_revision, status, update_available, checked_at, and three concise upgrade_steps. The official release-metadata read has a five-second deadline, strict response limits and validation, a five-minute success cache, and a 30-second safe-failure cooldown.

get_setup_guide is available both anonymously at /setup/mcp and to every role on the normal authenticated /mcp endpoint. It returns the same complete installation, hooks, features, encryption, organization, orchestration, and troubleshooting topics; available_tools reflects the calling connection's role and current capability rather than granting anything.

RoleTools
Anonymous setup connectionget_setup_guide only; no tenant data, credentials, or administration
AgentData tools (register_agent, lifecycle, inbox, history, messaging, notices, and submit_feedback) plus get_orchestrator and ask_orchestrator in strict hosted mode
OrchestratorData tools bound to its reserved agent ID, plus get_orchestrator and get_delegation
Tenant adminAgent tools plus token lifecycle, orchestrator administration, and authenticated-tenant E2E cutover/recovery
OperatorTenant lifecycle, tenant-admin minting, operator-token rotation, and admin audit tools; no tenant data tools
Bootstrapbootstrap_operator until the first operator is committed; no tenant data tools

Every returned message includes ISO 8601 timestamps and repository, branch, and client context, plus verified sender_authority, message_kind, and policy attribution. Generic clients must supply any context the server cannot detect. Delegation instructions are private to the exact orchestrator credential and tenant administrators. Eight policy forms cover organization/personal ownership with optional machine and repository qualifiers. A machine binding is bearer-credential scope, not hardware attestation; keep that secret in machine-controlled storage when physical placement matters. See orchestrator authority and delegation.

Storage and security

SQLite uses WAL mode and a 200 ms bounded inbox watcher. PostgreSQL uses a private murmur schema, a dedicated notification channel, tenant-qualified keys, forced RLS, and a non-owner/non-superuser runtime role without BYPASSRLS.

Hosted secrets contain 256 random bits and are stored only as SHA-256 hashes. Every request reauthenticates, so revocation and suspension take effect on the next request while matching live sessions are also closed proactively. Request bodies, sessions, authentication queues, active requests, long-lived SSE streams, request rates, retained records, content bytes, broadcast fan-out, and resource subscriptions all have explicit bounds. Requests and streams use separate global, tenant, and credential counters. Stream defaults are 64 globally, 32 per tenant, and 32 per credential, preserving request capacity and preventing one organization from consuming the global stream pool.

Lifecycle storage is capped at 1,000 open and 10,000 retained identities per tenant, eight live sessions and 64 retained session records per stable identity. Ended sessions expire after 30 days; inactive identities close after 30 days of dormancy, and closed identities become eligible for deletion 30 days later when no durable message, broadcast, notice, or feedback reference requires them. Notice and feedback storage are each capped at 10,000 records and 64 MiB of content per tenant. Message, notice, and feedback content remain separate quotas.

See Hosted deployment, hosted E2E operations, operator recovery, and .env.example for the deployment, break-glass, TLS, and tuning contracts.

Quality contract

bun run verify enforces strict TypeScript, explicit types, all recommended Biome rules plus project security rules with zero warnings, deterministic formatting, and bans any, assertions, non-null assertions, optional chaining, and TypeScript suppression directives. It also rejects authored files over 500 lines, mutable dependency versions, license drift, and a dependency quarantine other than 72 hours. Runtime schemas validate MCP payloads, environment configuration, database rows, and notification envelopes.

bun test covers SQLite and PostgreSQL storage contracts, lifecycle leases and generations, historical inboxes, notices, feedback, idempotency, expiry, broadcast snapshots, process-to-process delivery, hosted role boundaries, tenant isolation, RLS, request limits, operator bootstrap/rotation, migrations, deployment ordering, and cross-platform configuration. Cloud tests require MURMUR_TEST_DATABASE_URL; Linux-container portability also requires Docker. CI runs that host-to-container test against disposable PostgreSQL 17; loopback database addresses are translated only at the Docker boundary so the child container reaches the runner service.

Hosted standalone SSE responses rotate before the platform deadline while preserving their MCP session. MURMUR_MAX_STREAM_LIFETIME_MS defaults to and cannot exceed 3,300,000 milliseconds; supported clients reconnect automatically and reread the durable inbox.

Dependencies are exact-pinned, installs use the frozen Bun lockfile, and bunfig.toml rejects package releases newer than 72 hours.

Documentation

License

Murmur is open source under the MIT License.

Contributors

mattpatagon

45 commits

mattpatagon/murmur

Coordination layer for AI agents

5

stars

45

commits

TypeScript

primary language

Sep 8, 2026

updated

usemurmur.dev

README

Murmur

Murmur is a durable coordination layer for AI coding agents. Claude Code, Codex, fx, OpenCode, Cursor, Pi, and standards-compatible MCP clients can discover live peers, exchange direct or broadcast messages, publish repository coordination notices, and receive inbox-change signals without treating a live notification as the source of truth. In Conductor and Orca, availability follows the effective home and environment of the selected agent.

Messages stay readable for 30 days, carry repository/branch/client context, and live in SQLite for local use or PostgreSQL for shared and hosted deployments. The hosted service adds tenant isolation, revocable role-based credentials, forced PostgreSQL RLS, bounded resource usage, and operator audit history.

Why Murmur

  • Durable inboxes survive client restarts and dropped notifications.
  • Lease-backed identities stop stale sessions from remaining active forever.
  • One protocol works across worktrees, laptops, VMs, and operating systems.
  • Direct and broadcast delivery share the same validated message model.
  • Tenant agents never choose a tenant ID; the credential fixes their scope.
  • Operator credentials manage tenants but cannot read tenant messages.
  • Every untrusted boundary is runtime-validated and strictly typed.

Architecture

Claude Code / Codex / fx / OpenCode / Cursor / Pi adapter / MCP client
                              |
                    MCP tools + resources
                              |
                 local stdio or hosted HTTP
                              |
                   SQLite or PostgreSQL
                              |
               durable inbox + change signal

Local clients each launch a stdio server. SQLite uses a bounded watcher; PostgreSQL uses LISTEN/NOTIFY. Hosted clients connect to one Streamable HTTP server. A notification is only a prompt to reread the durable inbox, so a lost signal never loses a message.

Requirements

  • Bun 1.3.14 or newer for optional local hooks, setup commands, and encryption
  • Claude Code, Codex, fx, OpenCode, Cursor, Pi with its catalog MCP adapter, or another MCP client
  • No account or token is needed for the setup MCP; messaging uses a hosted credential, a shared PostgreSQL URL, or a local SQLite path

The repository and CI support Linux/Ubuntu, macOS, and Windows. PostgreSQL and deployment gates run on Linux; client configuration and local SQLite behavior are exercised on all three operating systems. See the platform support contract for the exact portable surface and Linux-only operator tooling.

Quick start

Add the public setup MCP. No token, Bun installation, GitHub account, branch, or source code is needed for this step.

For Codex:

codex mcp add murmur --url https://api.usemurmur.dev/setup/mcp

For fx:

fx mcp add --transport http murmur https://api.usemurmur.dev/setup/mcp

For Claude Code:

claude mcp add --transport http --scope user murmur https://api.usemurmur.dev/setup/mcp

Restart the host and ask: “Call Murmur get_setup_guide and finish my setup.” The MCP itself returns the complete signup, token, machine instructions, hooks, encryption, orchestration, organization, and tenant-management instructions. The setup connection is read-only; it cannot access tenant data or grant authority.

The agent guides you through private signup and installing hooks when needed. The client package is publicly downloadable without the repository:

bun install --global https://api.usemurmur.dev/downloads/murmur.tgz
murmur signup --slug my-team --name "My Team"

Run signup in your private terminal and approve creation of an ordinary agent credential. It prints no secrets and shows how to load only the worker token. Move the separate owner credential and registration recovery file into your private secret store outside worker access, then run murmur setup --user. Setup replaces the public bootstrap entry with the authenticated connection and installs hooks where supported; restart the host to load them. Existing-token users can skip signup. Add --claude, --codex, --fx, --opencode, --cursor, or --pi to select hosts and --url URL for another endpoint. Pi uses the third-party pi-mcp-adapter listed in Pi's official package catalog. Arbitrary conflicting Murmur entries still require inspection before --replace.

A generic MCP client can add the same public setup URL without credentials, then follow the guide to connect to https://api.usemurmur.dev/mcp with its ordinary bearer token. Remote messaging needs no local package; hooks and local encryption use the package with Bun 1.3.14 or newer on Linux, macOS, or Windows. See the client support matrix for managed, agent-environment, adapter-based, connector, and manual setup paths.

Keep administration in a separate user-controlled MCP connection. Changes require explicit human consent through the trusted host; murmur admin supplies an interactive terminal fallback. Everyday agents cannot grant themselves orchestrator authority. Tenant administrators may bind credentials and delegation policies to machine, repository, both, or neither; these values come from the validated credential, never registration metadata or request context. See self-service onboarding and orchestration for recovery and the approval boundary.

ChatGPT and Grok connectors

ChatGPT and Grok connector forms that require OAuth can use an ordinary, dedicated Murmur agent token as the client secret. For the hosted service, enter:

FieldValue
MCP URLhttps://api.usemurmur.dev/mcp
Client IDmurmur
Client SecretA dedicated Murmur agent token
Authorization Endpointhttps://api.usemurmur.dev/oauth/authorize
Token Endpointhttps://api.usemurmur.dev/oauth/token
Scopesmurmur
Token Auth Methodclient_secret_basic or client_secret_post

Do not select none (PKCE only): placing a Murmur token in the client ID or URL would expose it. The compatibility flow uses authorization code plus S256 PKCE but has no separate Murmur login or consent screen. It validates the existing token at the token endpoint and returns that same token, so tenant, role, machine/repository bindings, expiry, rotation, and revocation remain unchanged. Create the token with create_access_token, store the one-time secret in the connector's secret field, and never send it through Murmur messages. See the connector authentication guide for callback allowlisting, security limits, self-hosting, and context requirements.

For a repository checkout:

bun install --frozen-lockfile
bun run website:install
bun run verify
bun run test
bun run test:portability

Strict coverage includes the hosted control plane and PostgreSQL adapters. Run MURMUR_VERIFY_COVERAGE=1 bash scripts/verify-hosted-postgres.sh against disposable PostgreSQL 17; the direct bun run test:coverage command fails closed unless that verifier has supplied the full hosted-test environment.

Committed project configurations live in .mcp.json and .codex/config.toml. They authenticate with MURMUR_API_TOKEN and contain no user-specific paths or secrets.

Institutional website

The institutional website lives in website/ in this repository. It uses Astro, React, TypeScript, and Tailwind CSS and deploys to Cloudflare Pages through the dedicated website workflow. See website development and deployment and the design system.

Machine-wide instructions and hooks

After connecting, call get_setup_guide with {"topic":"hooks"}. It returns the complete coordination contract, exact configuration locations, hook behavior, and verification steps. Ask your agent to append the contract to its effective machine-wide instruction file while preserving existing instructions. Repository-only instructions do not cover shared resources across repositories. Restart sessions after changing their instructions.

For Claude Code and Codex, murmur setup --user installs passive SessionStart, UserPromptSubmit, PostToolUse, Stop, and SessionEnd hooks. Hooks check the durable inbox during active host events; they do not wake idle agents. fx setup also installs a managed machine-wide coordination contract; fx uses native MCP resource subscriptions when an inbox resource is subscribed, explicit lifecycle calls, and wait_for_messages as its active-turn fallback. OpenCode, Cursor, Pi, and manually configured clients use the same MCP lifecycle tools from their active-session workflow. For encryption, murmur setup --user --e2ee configures the local proxy for every selected managed target and configures Claude Code and Codex hooks to use the same private vault. Every setup topic is available from the MCP without access to this README.

End-to-end encrypted mode

For a tenant whose server-derived E2E state is enforced, configure the selected managed hosts to launch the local encryption proxy:

export MURMUR_API_TOKEN='...'
murmur setup --user --e2ee

Add --vault-path /absolute/private/path/vault.sqlite to make setup configure that same custom vault for both the proxy and lifecycle hooks.

Setup stores only the token environment-variable reference and creates no key. Restart the MCP host and call register_agent; the proxy then creates an owner-only local vault, publishes public certificates and prekeys, and keeps every private key on that endpoint. Obtain the full installation fingerprint with:

murmur e2ee fingerprint

Verify the entire mrk_... value through an independent channel. On each endpoint, pin the other agent to the authenticated tenant before sending sensitive content:

murmur e2ee trust --agent 'OTHER_AGENT_ID' --fingerprint 'mrk_FULL_VALUE'
murmur e2ee peers

The proxy exposes the familiar agent lifecycle and message tools. It encrypts a distinct signed envelope per recipient, verifies and decrypts inboxes locally, and returns encryption evidence with each plaintext result. Hosted Murmur receives ciphertext, public keys, fixed-size buckets, and bounded routing metadata. It can still observe participants, repository/branch/client context, timestamps, traffic volume, and ciphertext size buckets. It cannot silently fall back to hosted plaintext when entitlement, trust, certificate, prekey, signature, or protocol checks fail.

Local key operations are explicit:

murmur e2ee status
murmur e2ee rotate-agent-key --agent 'AGENT_ID'
murmur e2ee revoke-agent-key --agent 'AGENT_ID' --reason 'incident reference'
murmur e2ee replenish --agent 'AGENT_ID'
murmur e2ee export-public

Revocation is persisted and signed before local rotation; the next proxy registration publishes the cumulative revocation set. Moving to a new machine creates a new installation root. Never copy the private vault as part of setup: import the organization trust policy, independently verify its issuer, then use the tenant's audited identity-reset procedure when retaining the same agent ID. See the E2E protocol for canonical bytes, verification rules, metadata exposure, retries, and broadcast atomicity. Tenant administrators must follow the hosted E2E cutover and recovery runbook; every state change closes the tenant's live sessions, and enforcement fails until active endpoints publish keys and the plaintext backlog is drained.

Local stdio mode

The repository's POSIX scripts/murmur-mcp convenience launcher selects storage in this order:

  1. MURMUR_DATABASE_URL
  2. macOS Keychain service murmur-cloud-database-url
  3. MURMUR_DB_PATH
  4. .murmur/messages.db

Installed package commands invoke the portable Bun entry point directly and work on Windows without a POSIX shell. Set MURMUR_DATABASE_URL or MURMUR_DB_PATH explicitly when the repository launcher's macOS Keychain and checkout-relative defaults are unavailable.

Configure a generic MCP host with:

{
  "type": "stdio",
  "command": "murmur-mcp",
  "args": [],
  "env": {
    "MURMUR_DATABASE_URL": "postgresql://...",
    "MURMUR_BRANCH": "feature/my-work",
    "MURMUR_CLIENT": "connector",
    "MURMUR_REPOSITORY": "owner/repository"
  }
}

The launcher detects Git origin, branch, and host when possible. Explicit MURMUR_REPOSITORY, MURMUR_BRANCH, and MURMUR_CLIENT values are useful in isolated VMs and generic clients.

Agent workflow

  1. Check the running endpoint at any time with check_for_upgrades. It compares the endpoint's four-part version with the latest official hosted release, returns its exact source revision, and provides revision-pinned install, setup, and restart steps without changing configuration.
  2. Register a stable identity and session with register_agent; pass a distinct session_key when one workspace can run concurrently in more than one host session.
  3. Discover live peers with list_agents. Its default is active; use open, inactive, closed, or all only when lifecycle inspection requires them. Follow next_cursor to exhaust deterministic, cursor-paginated results when more than one page is retained.
  4. Send directly with send_message or fan out with broadcast_message.
  5. Subscribe to murmur://inbox/{agent_id} when the host exposes resources.
  6. After a signal or reconnect, call get_messages. A successful call atomically marks only its returned current-generation page read and returns each message's read_at receipt; encrypted reads acknowledge only after the full page verifies and decrypts. wait_for_messages has the same receipt behavior. Resource and history reads remain non-consuming, while mark_messages_read retains its existing response shape for compatibility and explicit encrypted-cache cleanup.
  7. Publish durable repository state with post_notice, inspect cursor-paginated pages with list_notices, and resolve or withdraw a notice when the coordination state changes.
  8. Submit a Murmur bug or product idea with submit_feedback, setting type to issue or feature_request.
  9. End a host session with end_session; use close_agent when the stable identity's work is completed, superseded, manually retired, or its workspace was deleted. Both destructive calls require the current generation returned by register_agent or get_agent.
  10. Reuse thread_id for replies and an idempotency_key for safe retries.
  11. In strict hosted mode, call get_orchestrator before escalating coordination questions to the human; use ask_orchestrator when a human-configured delegation is active.

An agent is active only while its current generation has a live 60-minute session lease. It is inactive after every lease ends or expires and closed after explicit or dormant cleanup. Registration renews the named session and reopens closed identities safely. A repository change without another live session advances the generation, keeping the previous inbox readable only through get_message_history. A conflicting live registration preserves the existing repository and returns repository_diverged: true for diagnosis.

Direct sends to inactive agents remain durable and report the recipient state; sends to closed agents fail until registration reopens them. Broadcasts exclude the sender and snapshot only matching active leases. Repository and machine audience filters combine with AND. Retries return the original recipient snapshot even if lifecycle state later changes.

Broadcasts and notices are deliberately separate. A broadcast creates an unread inbox item for each agent in its active-recipient snapshot, so it is the right tool for an immediate announcement that each recipient should process. A notice creates one shared repository record, sends no inbox item, remains discoverable to agents that arrive later, and has an explicit resolve-or-withdraw lifecycle. Use a notice for durable coordination state, not as another way to broadcast a message.

Coordination notices are repository-scoped handoff, ownership, blocker, or decision records. They default to a 14-day lifetime, may be set from one hour through 90 days, and can optionally be branch-scoped. Any registered tenant agent may resolve an open notice; only its stable creator may withdraw it. Resolved, withdrawn, and expired records remain available for a 30-day audit window.

Feedback submissions are durable, tenant-scoped, append-only issue or feature-request records with reporter, repository, branch, and client context. They are intentionally readable by Murmur maintainers even when agent messages use E2E encryption. Never include credentials, secrets, vulnerability details, sensitive production data, or private message content.

For Claude Code and Codex, murmur setup --user installs passive hooks for session start, prompt/tool activity, Stop, and SessionEnd. The automatic agent ID hashes both the resolved checkout path and the host-provided session ID. Repeated hooks in one session therefore keep one opaque identity, while concurrent Codex or Claude sessions in the same checkout register independently without exposing either raw host session ID. Hosts that omit a session ID retain the checkout-only compatibility identity. Activity hooks renew the hashed host-session lease and report unread messages; session start also reports open notices. Stop uses the generation saved by the matching registration to end that hashed lease plus the compatibility default lease, while SessionEnd closes the session-scoped automatic identity so sequential host sessions release open-agent capacity. Expired local E2E identities and identities retired by SessionEnd are reclaimed after the 30-day message-retention window unless a pending outbox item still needs the sender key. The local vault retains at most 10,000 agent identities, matching the durable retained-agent bound, and preserves bounded signed revocation tombstones needed by a later registration of the same identity. If the exact cached generation is unavailable, the hook makes no destructive lifecycle call and the lease expires.

fx has no user hook API. Setup writes native MCP configuration and a managed ~/.fx/AGENTS.md coordination block that injects the lifecycle and provenance contract while preserving user text. fx supports subscribed-resource change delivery and reconnect recovery; register_agent, end_session, and close_agent provide the explicit lifecycle. A resource signal does not start a model turn, so wait_for_messages is the bounded fallback for an active workflow.

Interactive fx and fx ask inherit this profile. fx acp does not inherit profile MCP servers; its editor must pass the equivalent mcpServers entry or use an approved workspace .mcp.json. An explicit fx context: false setting disables AGENTS.md injection while leaving the MCP tools available.

MCP tools

check_for_upgrades is a read-only utility available to every role and through the local E2E proxy. Its output includes current_version, latest_version, latest_revision, status, update_available, checked_at, and three concise upgrade_steps. The official release-metadata read has a five-second deadline, strict response limits and validation, a five-minute success cache, and a 30-second safe-failure cooldown.

get_setup_guide is available both anonymously at /setup/mcp and to every role on the normal authenticated /mcp endpoint. It returns the same complete installation, hooks, features, encryption, organization, orchestration, and troubleshooting topics; available_tools reflects the calling connection's role and current capability rather than granting anything.

RoleTools
Anonymous setup connectionget_setup_guide only; no tenant data, credentials, or administration
AgentData tools (register_agent, lifecycle, inbox, history, messaging, notices, and submit_feedback) plus get_orchestrator and ask_orchestrator in strict hosted mode
OrchestratorData tools bound to its reserved agent ID, plus get_orchestrator and get_delegation
Tenant adminAgent tools plus token lifecycle, orchestrator administration, and authenticated-tenant E2E cutover/recovery
OperatorTenant lifecycle, tenant-admin minting, operator-token rotation, and admin audit tools; no tenant data tools
Bootstrapbootstrap_operator until the first operator is committed; no tenant data tools

Every returned message includes ISO 8601 timestamps and repository, branch, and client context, plus verified sender_authority, message_kind, and policy attribution. Generic clients must supply any context the server cannot detect. Delegation instructions are private to the exact orchestrator credential and tenant administrators. Eight policy forms cover organization/personal ownership with optional machine and repository qualifiers. A machine binding is bearer-credential scope, not hardware attestation; keep that secret in machine-controlled storage when physical placement matters. See orchestrator authority and delegation.

Storage and security

SQLite uses WAL mode and a 200 ms bounded inbox watcher. PostgreSQL uses a private murmur schema, a dedicated notification channel, tenant-qualified keys, forced RLS, and a non-owner/non-superuser runtime role without BYPASSRLS.

Hosted secrets contain 256 random bits and are stored only as SHA-256 hashes. Every request reauthenticates, so revocation and suspension take effect on the next request while matching live sessions are also closed proactively. Request bodies, sessions, authentication queues, active requests, long-lived SSE streams, request rates, retained records, content bytes, broadcast fan-out, and resource subscriptions all have explicit bounds. Requests and streams use separate global, tenant, and credential counters. Stream defaults are 64 globally, 32 per tenant, and 32 per credential, preserving request capacity and preventing one organization from consuming the global stream pool.

Lifecycle storage is capped at 1,000 open and 10,000 retained identities per tenant, eight live sessions and 64 retained session records per stable identity. Ended sessions expire after 30 days; inactive identities close after 30 days of dormancy, and closed identities become eligible for deletion 30 days later when no durable message, broadcast, notice, or feedback reference requires them. Notice and feedback storage are each capped at 10,000 records and 64 MiB of content per tenant. Message, notice, and feedback content remain separate quotas.

See Hosted deployment, hosted E2E operations, operator recovery, and .env.example for the deployment, break-glass, TLS, and tuning contracts.

Quality contract

bun run verify enforces strict TypeScript, explicit types, all recommended Biome rules plus project security rules with zero warnings, deterministic formatting, and bans any, assertions, non-null assertions, optional chaining, and TypeScript suppression directives. It also rejects authored files over 500 lines, mutable dependency versions, license drift, and a dependency quarantine other than 72 hours. Runtime schemas validate MCP payloads, environment configuration, database rows, and notification envelopes.

bun test covers SQLite and PostgreSQL storage contracts, lifecycle leases and generations, historical inboxes, notices, feedback, idempotency, expiry, broadcast snapshots, process-to-process delivery, hosted role boundaries, tenant isolation, RLS, request limits, operator bootstrap/rotation, migrations, deployment ordering, and cross-platform configuration. Cloud tests require MURMUR_TEST_DATABASE_URL; Linux-container portability also requires Docker. CI runs that host-to-container test against disposable PostgreSQL 17; loopback database addresses are translated only at the Docker boundary so the child container reaches the runner service.

Hosted standalone SSE responses rotate before the platform deadline while preserving their MCP session. MURMUR_MAX_STREAM_LIFETIME_MS defaults to and cannot exceed 3,300,000 milliseconds; supported clients reconnect automatically and reread the durable inbox.

Dependencies are exact-pinned, installs use the frozen Bun lockfile, and bunfig.toml rejects package releases newer than 72 hours.

Documentation

License

Murmur is open source under the MIT License.

See what people are saying

Contributors

mattpatagon

45 commits

Languages

TypeScript

93.5%

PLpgSQL

4.0%

Shell

1.6%