Getting started

A first vault in five minutes, then a second device.

Ten minutes: a project in a vault, a second device, a pull request through the local UI. To watch all of it happen first without touching anything of yours, run secretree demo.

1. A vault for a project

In a repository you already have:

$ cd ~/code/app
$ secretree init --vault git@gitlab.com:you/app-vault.git --kit-out ~/Desktop/app-kit.txt --push
vault:   git@gitlab.com:you/app-vault.git (id 7cac93dda9ebaed9, branch main)
repo:    app (id 02ed7a372737891b)
keys:    macOS Keychain (service secretree)
config:  /Users/you/code/app/.git/secretree/config.json

Recovery kit written to /Users/you/Desktop/app-kit.txt. PRINT IT, then delete the file.
When it is on paper, run: secretree kit --confirm   (status warns until then)
helper:  git-remote-secretree ready
remote:  origin = secretree::git@gitlab.com:you/app-vault.git

pushing every branch and tag through the helper…
 * [new branch]      main -> main

What happened: a fresh age identity and an Ed25519 signing key went into your key store; the empty remote became a vault (a README.md, a signed vault.json listing your public keys); origin now points at the vault through the helper; every branch and tag was encrypted into a first generation and pushed.

The remote must exist and be empty. On GitHub or GitLab, secretree init --vault github:you/app-vault creates it for you as a private repository when the gh (or glab) CLI is installed and logged in.

The recovery kit is the only way back after losing every device. Print it, confirm it, delete the file:

secretree kit --print ~/Desktop/app-kit.txt
secretree kit --confirm
rm ~/Desktop/app-kit.txt

2. Work as usual

$ git checkout -b feature/retry
$ git commit -am "add retry helper"
$ git push origin feature/retry

Each push becomes one generation: an incremental git bundle encrypted to every member, a signed manifest, one commit on the vault. git fetch and git pull replay what you have not seen. The mirror the helper maintains lives in .git/secretree/mirror; you never touch it.

$ secretree status
repository:      /Users/you/code/app (app, id 02ed7a372737891b)
vault:           git@gitlab.com:you/app-vault.git (id 7cac93dda9ebaed9, branch main)
keys:            present in macOS Keychain (service secretree)
generations:     3 (4.1 KiB on the remote)
last backup:     generation 000003, 2026-09-18 10:12 (2m ago)
last proven:     never

"last proven" refers to the full restore proof that secretree backup and secretree verify perform; pushes are verified on every fetch.

3. A second device or a teammate

On the new machine:

$ secretree join --vault git@gitlab.com:you/app-vault.git --name laptop --out join.txt
keys stored in macOS Keychain (service secretree). Send this join request to a current member:
…

The join request contains only public keys. A current member approves it:

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

Then, on the new machine:

$ secretree clone git@gitlab.com:you/app-vault.git app
Cloning into 'app'...

The new member can read everything from the full generation written at approval onward; it carries the complete git history. Earlier backup snapshots stay opaque to them.

4. A pull request

$ secretree policy --approvals 1 --checks ci
wrote .secretree/policy.json; commit it on the base branch to enforce it
$ git add .secretree && git commit -m "review policy" && git push origin main

$ secretree pr open --title "Add retry helper" --body "Wraps flaky calls."
pushing feature/retry to origin…
pull request #1 opened: Add retry helper (feature/retry → main, 900c2a69)

A teammate reviews, in the local UI or on the command line:

$ secretree pr comment 1 -m "Should the delay grow between attempts?" --path src/retry.go --line 12
$ secretree pr approve 1 -m "Fine as a first version."

CI runs on a machine that holds a key (any member's machine, or a dedicated box):

$ secretree runner --once
runner: #1 feature/retry @ 900c2a69 → success (./.secretree/ci passed in 4s)

Merge, with the policy enforced from signed inputs:

$ secretree pr merge 1
#1 merged into main as 4ebfacfe

5. Look at it

$ secretree ui --open

opens http://127.0.0.1:7391: branches, files with highlighting and line anchors, blame, history, search across every branch, the pull request pages with the diff, inline comments, checks and the merge button, the vault as the host sees it, and the disclosure ledger. secretree ui --install keeps it running in the background so permalinks always resolve.

What to do next