Murmurent
Shared agentic-AI infrastructure for academic researchers, labs cores and research centers.
It lets research groups work independently, pool agents and data when collaboration helps, and
accumulate institutional knowledge across every project. Documentation: https://hallettmiket.github.io/murmurent/ (install,
getting-started vignettes, the agents, labs and centres, the CLI manual).
CLAUDE.md is the architectural overview Claude Code itself loads.
Murmurent can be used as a standalone agentic AI OS environment, as a means to integrate members of the same lab, or as a means of integrating labs and core facilities across a centre or University.
Stuck on any step below? Once you've installed Claude Code, you can just ask it. Murmurent wires its own docs and CLI into Claude Code, so "walk me through installing Murmurent", "did my install work?", or "how do I issue a member card?" all work — Claude Code can run many of these steps for you.
[Everyone] Download Murmurent
Two ways in. Both end up in the same place; pick one.
A. From PyPI (recommended)
Nothing to clone, and you never pipe a script into your shell. If you don't have uv yet, install it first — one line:
curl -LsSf https://astral.sh/uv/install.sh | sh # skip if you have uv
Then:
uv tool install murmurent
murmurent install
If your shell can't find murmurent afterwards, close and reopen your terminal
(uv puts it in ~/.local/bin).
B. One command
Only needs git. This one installs uv for you if it's missing:
curl -fsSL https://raw.githubusercontent.com/hallettmiket/murmurent/main/scripts/bootstrap.sh | bash
To read the installer before running it, clone the repo first and run
scripts/bootstrap.sh from inside it.
Either way you need Python 3.12 or newer; uv downloads one automatically if your system hasn't got it, so you don't need to install Python yourself.
This installs the murmurent command, wires the shared agents/rules/skills into ~/.claude/, and
registers the data-governance hooks. On your first run it mints your identity
key (your unique ID). Then set your personal info — murmurent whoami shows your
handle + key.
Check the install, any time
murmurent doctor
It checks the Python version, that pip and python on your PATH belong to the
same interpreter Murmurent runs under, the agent/rule/skill links in ~/.claude/,
the registered hooks, and (for a clone) that git pull will work. Every problem
it finds comes with the one command that fixes it.
Upgrading
Installed from PyPI:
uv tool upgrade murmurent
murmurent install
Installed from a clone:
cd ~/repos/murmurent && git pull
uv tool install --python 3.12 --reinstall -e .
murmurent install
Then, either way:
murmurent doctor # confirms the upgrade landed
murmurent repo upgrade --all # brings every Murmurent-ready directory up to the new release
Use uv for the reinstall. It installs into the interpreter Murmurent already
runs under. The pip on your PATH can belong to a different Python (a conda
base environment, for example), and then it either installs into the wrong
place or stops with Package 'murmurent' requires a different Python.
Two repositories: which one am I looking at?
murmurent |
Releases. One commit per release, no development history. This is what you install, and what the instructions above clone. |
murmurent_dev |
Development. All history, issues, pull requests and design discussion. Has a DEVELOPING.md the release does not. |
They share this README, so the quickest way to tell them apart is the file
list: if you can see DEVELOPING.md and a tests/ directory, you are in
murmurent_dev.
- Using murmurent? Install from either the PyPI or the one-command route above; both give you the latest release.
- Reporting a bug or asking for a feature?
Open an issue on
murmurent_dev. The release repo has issues turned off, because the discussion belongs where the work happens. - Working on murmurent itself? Clone
murmurent_devand read itsDEVELOPING.md: dev setup, whatrules/local/is for, the pre-push checks, and how a release is cut. Do not develop against the release repo — it has no tests and no history.
[Everyone] Set up your identity
murmurent init # sets your handle, name, email, official handle, GitHub, Slack (choose member / PI / mayor)
The init records who you are: your handle/name/email/official (institutional)
handle/GitHub/Slack; everything else builds on it, whether or not you ever
join a lab/core.
You have a choice to be either (i) a user (termed a 'member'), (ii) a PI who leads
a lab or core facility, or (iii) a mayor who runs a centre (which consists of multiple labs
and cores). You have to specify one of these three options during the init procedure.
You're ready to run Murmurent locally. Several vignettes can help get you started Getting started.
[Everyone] Initialize a directory for Murmurent
Murmurent works inside a repository: a directory tracked by git, kept under
~/repos/. Making a directory Murmurent-ready wires the shared agents and
rules into it, so Claude Code sessions opened there can use them. The same
procedure covers a brand-new folder, a repository you have worked in for years,
and one that an older Murmurent release set up. Start by asking, because the
answer decides the step:
murmurent repo status ~/repos/<directory>
| Verdict | What it means | Do this |
|---|---|---|
not a git repo |
a plain folder | git -C ~/repos/<directory> init, then the next row |
plain clone or partial |
git, and Murmurent has never set it up | murmurent repo adopt ~/repos/<directory> |
ready, bootstrapped by an older version |
ready, and newer agents are missing | murmurent repo upgrade ~/repos/<directory> --all-agents |
ready, current version |
finished | open Claude Code in it |
Adopting writes a .murmurent.yaml marker and a .claude/agents/ folder of
symlinks into the commons, and leaves every other file as it was. Commit both,
so each clone of the repository is ready as well. murmurent repo list shows
the verdict for every repository on the machine, and murmurent repo upgrade --all upgrades all of them at once. Details, and how ready repositories are
attached to a project: Making a repo Murmurent-ready.
Federating individuals, groups and centres
Murmurent allows members to join labs or cores, and it allows labs/cores to join centres. This is based on cryptographic identity cards that establish your identity and "right to belong".
[Members] If you are a member of a lab whose PI already uses murmurent
You need a membership ID (a signed identity certificate) from your PI to include you in the lab or core. You need to be in the Slack workspace of your PI. You will also need the official name of your lab or core.
-
Request your ID:
murmurent enroll --group <your-lab> --out enroll.json
Send the output file
enroll.jsonto your PI — DM it to them directly on Slack. -
The PI then runs
murmurent issue-member-cardagainst your request. Murmurent will DM the signed bundle back to you. -
Save what you received as a file (e.g.
bundle.json). It looks like this (trimmed):{ "member_card": { "payload": {"subject": {"handle": "@allie", "fingerprint": "SHA256:jo8Aqfe6In..."}, "group": "xia_lab"}, "signature": "..." }, "pi_card": { "payload": {"subject": {"handle": "@yxia266", "pubkey": "ed25519:Rgmuqeen5X3lW4pFV8GHVFafw0ozSxGk+uUeLC279Fw="}}, "signature": "..." } }
The trust root is that
pubkeyvalue insidepi_card—ed25519:Rgmuqeen5X3lW4pFV8GHVFafw0ozSxGk+uUeLC279Fw=. It's a short string. Confirm that trust-root value with your PI out-of-band (in person or by phone, not the same Slack message). You must pass it explicitly in theimport-cardcommand next:murmurent import-card bundle.json --trust-root ed25519:Rgmuqeen5X3lW4pFV8GHVFafw0ozSxGk+uUeLC279Fw=
-
Confirm it worked — you don't need to keep the output:
murmurent whoami # now lists your group and role
import-cardstores the verified card locally, so from now on Murmurent knows you're a member of the lab. -
Clone your lab's governance repository. Your card proves who you are; the roster of everyone else — and the lab's projects, compliance records, and shared Oracle — lives in a separate private repository that every member holds a read-only clone of. Ask your PI for its name, then:
git clone git@github.com:<org>/murmurent_lab_mgmt_<lab>.git \ ~/repos/murmurent_lab_mgmt_<lab> murmurent member list # should now show the whole lab, not just you
Without this clone,
murmurent member listand the dashboard's members panel have nothing to read and will tell you so. Keep it current withgit pull(or the dashboard's update button); the PI pushes roster changes there. See The lab-mgmt repo.
[PIs] If you are a PI of a lab or core ...
Once you have completed your init, you need to set up some infrastructure
for your members.
- Connect your lab's Slack. This lets member IDs travel by DM instead
of by hand:
murmurent group-slack-setup <your-lab>
Full details regarding creating the Slack app with security scopes, etc.: Group Slack setup. - Accept members by issuing them IDs. A member runs
murmurent enroll --group <your-lab>and gets instructions to send you the resulting request (e.g. a Slack DM). Once you have it:murmurent issue-member-card <their-request> --group <your-lab>
This automatically DMs the signed bundle back to the member — pass--dm <slack_user_id>if you already know their Slack id, or--no-dmto skip Slack and just print the bundle. The member finishes withmurmurent import-card <bundle> --trust-root <your-trust-root>.
Full identity flow (enroll → issue → import → revoke): Membership IDs and the trust chain.
[PIs] If you are a PI registering your lab or core with an existing centre
If you want to join an existing Murmurent centre, you send the centre's mayor an encrypted join request, and they send you back a signed PI ID. Now:
- Find your centre in the public implementations directory —
murmurent_publiclists every institution running Murmurent, the address to send join requests to, and the public key your request is encrypted to. If your institution isn't listed, it may not run Murmurent yet. - Run the join script. It asks a few questions, encrypts your request to your
centre's key, and opens your email app ready to send:
curl -fsSL -O https://raw.githubusercontent.com/hallettmiket/murmurent_public/main/join/murmurent-join.sh sh murmurent-join.sh
The request is encrypted to your centre's Mayor — only they can read it, and nothing about you is posted publicly. Press Send. - Once the mayor approves, they send your PI ID back for you to import:
murmurent import-card <bundle> --trust-root <centre-trust-root>
Confirm the trust-root value with the mayor out-of-band before you rely on it.
Once you hold your PI ID, you are your lab's certificate authority.
[Mayors] If you want to establish a new Murmurent centre at your institution as the Mayor...
You'll need:
- Claude Code — installed and logged in once (OAuth).
- GitHub CLI
gh, authenticated (gh auth login) — for the centre's GitHub org/repos. - uv — the installer adds it if missing.
You bootstrap a new centre with
murmurent centre-init
and become its founding registrar — see the details below.
Only --name and --institution are required; everything else
is optional and can be filled in later from the dashboard or with
murmurent centre-set. A fully-worked example:
murmurent centre-init \
--name "Example Bioconvergence Centre" \
--institution "Example University" \
--mayor @the_mayor \
--unique-name example \
--join-email murmurent-join@example.edu \
--slack-workspace T0EXAMPLE \
--github-org centre-example \
--public-hub github.com/hallettmiket/murmurent_public#example \
--server-host lab-server.example.edu \
--server-account murmurent \
--cc-install-path /opt/claude \
--mayor-root /mayor/example \
--obsidian-vault /mayor/obsidian \
--raw-root /data/example/raw \
--refined-root /data/example/refined
murmurent centre-status # confirms you are the founding registrar
Each parameter, with an example:
| Flag | What it is | Example |
|---|---|---|
--name (required) |
Display name of the centre | "Example Bioconvergence Centre" |
--institution (required) |
Hosting institution | "Example University" |
--mayor |
Your @handle (defaults to $MURMURENT_USER, then the OS user) |
@the_mayor |
--unique-name |
Short, institution-agnostic id — drives repo / Slack / group names | example |
--join-email |
Public address PIs send join requests to (listed in the directory) | murmurent-join@example.edu |
--slack-workspace |
Your Slack workspace / team id (the T… id) |
T0EXAMPLE |
--github-org |
The centre's GitHub org / dedicated account | centre-example |
--public-hub |
Global onboarding hub + this centre's label | github.com/hallettmiket/murmurent_public#example |
--server-host |
The always-online, ssh-gated murmurent server | lab-server.example.edu |
--server-account |
SSH login account on that server | murmurent |
--cc-install-path |
Where Claude Code lives on the server | /opt/claude |
--mayor-root |
High-level mayor dir (mirrorable to GitHub) | /mayor/example |
--obsidian-vault |
Centre-level Obsidian / markdown pool | /mayor/obsidian |
--raw-root |
Centre raw/ root on the data server |
/data/example/raw |
--refined-root |
Centre refined/ root on the data server |
/data/example/refined |
--data-server is a legacy alias of --server-host. Add --no-prompt for
scripted / server runs, and --no-sentinel when running under sudo or in CI.
Make your centre joinable
We cannot assume that prospective members already belong to the Centre's Slack workspace. The next steps are as follows:
- Encryption key for join requests.
centre-initgenerates anagekeypair automatically so that PIs can encrypt their join requests to it; recreate withmurmurent centre-age-keygen. - Root signing key (the identity CA).
murmurent centre-root-keygen— signs PI IDs + the revocation list. Back it up offline (see The centre root key). - List your centre in the implementations directory:
murmurent centre-hub-publishclonesmurmurent_public, writes your directory row, and publishes your signing key + revocation list so members can verify IDs. It prints agit pushfor you to run. - Set up Slack. Create a
murmurent-<unique-name>workspace + bot token and smoke-test withmurmurent centre-slack-smoke. Guide: Centre Slack setup.
Authors
Mike Hallett — michael.hallett@uwo.ca
Release files for murmurent 2026.9.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| murmurent-2026.9.7.tar.gz | 2.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| murmurent-2026.9.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.4 MB
Release files / murmurent-2026.9.7.tar.gz
| Download URL | murmurent-2026.9.7.tar.gz |
|---|---|
| Size | 2.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
318d76f74336539738e8b30f99c4938c9adf9b36469403876155fec60dfd20f9
|
|
BLAKE2b-256 checksum How to use checksums |
880d2bce1a1745371993112ef89ff009646e5d3125585288d83e3df214d4baa3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.
Transparency logRelease files / murmurent-2026.9.7-py3-none-any.whl
| Download URL | murmurent-2026.9.7-py3-none-any.whl |
|---|---|
| Size | 998.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
568ca4c6a40dcc54173cbe5c76ab7ce633037fa5f6fcf153bbf62e33acd883d7
|
|
BLAKE2b-256 checksum How to use checksums |
82530a5a0cefeb91c6d08462266027f9bf9aa05c2c329aadc81613a119992cd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.
Transparency log