Team and agents

Joining, approving, revoking; AI reviewers as members.

Every device is a member with its own keys. People, second laptops, CI boxes and AI reviewers all join the same way; what they may do is decided at approval.

Joining from a new device

On the device that wants access:

$ secretree join --vault git@gitlab.com:team/app-vault.git --name bob-laptop --out join.txt
keys stored in macOS Keychain (service secretree); join request written to join.txt

Once they run `secretree member add --request <file>`, clone with: secretree clone git@gitlab.com:team/app-vault.git

join.txt contains the vault id, the device name, the age public key and the OpenSSH public key. Nothing secret. Send it to a current member over any channel.

If the device already holds keys for that vault (for example after a join whose request got lost), secretree member request prints the request again from the repository.

Approving

A member with write access:

$ secretree member add --request join.txt
member added: bob-laptop (age1n92…)
generation 000010 written (full) for the new recipient set

vault.json is re-signed with the new recipient and signer, and a full generation is written to the new recipient set right away, so the newcomer can clone the complete history. Backup snapshots made before this point stay opaque to them: their signatures and chain position are still verified, their contents are not readable by the new key.

Without a request file, the keys can be given directly:

$ secretree member add --name ci-box --recipient age1… --signer "ssh-ed25519 AAAA… ci-box"

--signer is optional; a member without one can read but not write.

Listing

$ secretree member list
vault 7cac93dda9ebaed9: 3 recipient(s), 3 signer(s)
  app              age1sdwe…  signer SHA256:ELUg…  (this device)
  bob-laptop       age1n92m…  signer SHA256:Nc3k…
  review-bot       age1l0y6…  signer SHA256:7Q2x…  [agent]

Removing and revocation

$ secretree member remove --name bob-laptop
member removed: age1n92…
generation 000012 written (full) for the new recipient set

The recipient is dropped, so every later generation is unreadable to that key. The signer line moves into revoked_signers with the last generation and ledger entry it may have signed: history stays verifiable, and a revoked key that still holds a push credential for the host cannot append anything other members will accept. The removed member's next git fetch fails.

What they already fetched stays theirs; zero knowledge cannot un-send.

Agents

An AI reviewer, a linter bot or any automation is a member with the agent role:

$ secretree member add --request review-bot.txt --role agent
agent added: review-bot (age1…); its approvals do not count and it cannot merge

Agents read, comment and review. Their approvals never count toward required_approvals, they cannot merge (even with a human approval present), and their comments carry an agent mark in pr show and in the UI. The agent runs as a runner job; a working reviewer built on the Claude CLI ships as agents/review.sh in the repository.

Several repositories in one vault

A vault may hold several repositories, each under its own random repo-id. secretree init in a second repository against the same vault adds one; secretree clone <vault>#<repo-id> and git clone secretree::<vault>#<repo-id> pick one when the vault holds more than one. All members of the vault see all its repositories; use separate vaults for separate access.