Share, ledger, notifications
Encrypted share pages, the disclosure ledger, watch.
Three things that make a zero-knowledge repository comfortable to live with: showing something to a person without a key, knowing exactly what has ever left the key boundary, and hearing about activity.
Share pages
$ secretree share src/retry.go --expires 3d --note "for the auditor"
encrypted share written: /Users/you/code/app/retry-go-4ebfacfe.html (5.4 KiB)
host that file anywhere; it is ciphertext. Send the key through a different channel:
key: AGE-SECRET-KEY-1053P7VJ…
link: file:///Users/you/code/app/retry-go-4ebfacfe.html#AGE-SECRET-KEY-1053P7VJ…
expires 2026-09-21T14:12:31Z (advisory: the viewer warns, the ledger records it)
recorded in the disclosure ledger (secretree ledger)
The output is one HTML file: a static viewer plus an age-encrypted snapshot of the file (or a diff with --diff a..b, or a commit with --diff <sha>) under a fresh one-off key. Put the file on any web host, S3 bucket or pastebin; it is ciphertext. The key travels in the link after #, which browsers never send to servers, or through a separate channel. The viewer decrypts in the browser using the age-encryption library from jsDelivr.
--ref picks the commit (default HEAD), --out the file name, --expires an advisory expiry the viewer warns about (0 for none). Binary files cannot be shared as text.
Revoke by deleting the hosted file. There is no server to log views, by design.
The disclosure ledger
Every share writes an entry; anything else that leaves the key boundary should too:
$ secretree ledger add --kind export --subject "coverage numbers → codecov" --note "weekly"
$ secretree ledger
2 disclosure(s), all signed and chained:
000001 2026-09-18 14:12 share file src/retry.go@4ebfacfe by alice expires 2026-09-21 (for the auditor)
000002 2026-09-18 15:03 export coverage numbers → codecov by alice (weekly)
Entries are encrypted to the members, signed by the acting device and hash-chained, exactly like generations: the ledger can be neither forged nor silently trimmed. The review agent writes an export entry before sending a diff to a hosted model. The product claim is precise because of this file: not "nothing ever leaves", but "nothing leaves without a signature and a log entry".
Permalinks
$ secretree link src/retry.go:12
http://127.0.0.1:7391/blob/4ebfacfe830704c8ca47ae322ffd7f420e428d81/src/retry.go#L12
The link opens in the local UI of anyone who holds a key and runs secretree ui. To make links that work from chat without knowing the port, host go.html (from the site repository) on any static host: https://links.example/blob/main/src/retry.go#L12 forwards the path to 127.0.0.1:7391, revealing at most a path hit to that host.
Notifications
$ secretree watch --desktop --ntfy https://ntfy.sh/team-app-x7q2
watch: 5 event(s) known, waiting for new ones
watch: PR #1: new comment by bob-laptop · PR #1: approved by bob-laptop
watch polls the vault every two minutes (--interval) and reports new pull requests, comments, reviews, checks and deploys by other members. Sinks: --desktop (macOS or Linux notification), --ntfy <topic url>, --exec <command> with the text in $SECRETREE_MESSAGE. Messages carry numbers and kinds only; --include-titles adds pull request titles, which then leave the key boundary. Your own events are never announced.
--serve :8787 also accepts host webhooks: point GitHub's or GitLab's push webhook for the vault repository at it, and any POST triggers an immediate check instead of waiting for the next poll. The webhook body is ignored; the host learns nothing beyond the fact that it delivered a hook.