Warning
Experimental software. bsecret is part of bstack, the applications and patterns built on BSV Layered Multicast. It is published to be built on and improved. Interfaces, formats and behavior may change rapidly between releases; pin an exact version.
Secrets management on Bitcoin, replicated by overlay hosts that hold ciphertext and never a key. A secret is encrypted once under a random key; the key is wrapped to the members of a group's current epoch; and when the group removes a member, the vault must re-key everything it holds before it does anything else.
The TypeScript host module under
host/is frozen: bug fixes only. A Go overlay engine will take its place; new behavior goes to the Go core.
- A secret is a keyed object. Each version is encrypted under its own random content key, committed to before anyone holds it (BRC-369). The ciphertext is public; the key is wrapped to the owner's wallet and under the key of one epoch of a borg group.
- Readers are a group's members. Adding a reader is adding a member. Removing one begins a new epoch whose key the removed member never receives.
- Revoke forces rotation, checked by every host. A vault is a chain of mined tokens. Once it acknowledges a new epoch, no host admits anything onto that chain but the re-keying or retirement of what it held, until nothing is left under an earlier epoch. Hosts cannot make an owner acknowledge: readers and auditors see a vault that has not.
- The chain is the audit trail. Anyone with block headers verifies which versions the owner listed, in what order, under which epoch, and how long each stayed listed under an epoch a removed member held. No cooperation from a host or the owner is needed. The trail proves what the owner signed; that a version's key is wrapped under the epoch it names is checked by the members who open it.
- Hosts are replicas, not authorities. A host can withhold; it cannot forge, alter or decrypt.
- Pseudonymous, not hidden. Values, names and claims are inside the ciphertext. The organization's key, the vault's and secrets' identifiers, the display name, labels, sizes and timing are public: on chain, and at the hosts unless a host makes the crypt private, answering only its organizations and their groups' members (who sees what).
- Honest about what removal does. A removed member keeps everything it could read. Removal protects what is written after the vault re-keys.
$ go install github.com/lightwebinc/bsecret/cmd/bsecret@latest
$ go install github.com/lightwebinc/borg/cmd/borg@latest # the owner also runs borgOr download a static binary for Linux or macOS (amd64, arm64) from
Releases, or build the
image ghcr.io/lightwebinc/bsecret (make docker-build).
The owner is a borg organization with a group that has run borg org signer once (borg). No node is
needed: by default bsecret reads from WhatsOnChain and broadcasts through
GorillaPool's public arcade, checking every proof against the headers; use
your own node where you have one. Nothing is mined for you: fund the
bsecret home with a payment from your own wallet.
$ bsecret -network main init -borg ~/.borg # a home around the organization's identity
identity 02c6…9a1e
fund address 1EUY…zipY (main)
$ # send about 20,000 sat from your own wallet to the fund address (a vault and ninety versions cost under 10,000)
$ bsecret fund -txid <txid of that payment> # once mined; or fund -beef FILE at once
$ bsecret crypt new example # give the printed BSECRET_CRYPTS line to your hosts
$ bsecret vault create engineering # a vault on a borg group; its display name is drawn at random
$ bsecret put db/password < new-password.txt # a version, listed in a mined token
$ bsecret get db/password | psql ... # a member reads, every check in order
$ borg org remove <key> -group engineering # the removal is borg's: a new epoch
$ bsecret rotate -set db/password=new.txt -same-all # acknowledge, re-key, sweep
$ bsecret -org <key> -vault <id> audit -free # anyone: the trail, from headersThe owner's bsecret home is made around the identity of its borg home and shares nothing else with it: the group's chain and its epoch keys are read from the hall's hosts by borg's own packages. Members and auditors need no coin. The full walk on mainnet or testnet is docs/examples.md; a sandbox on one machine that needs no coin is QUICKSTART.md.
| If you want to | Read |
|---|---|
| Run it on mainnet or testnet, step by step | docs/examples.md |
| Try it on one machine with no coin (a private regtest chain) | QUICKSTART.md |
| Understand the ideas and use it day to day | docs/user-guide.md |
| Set every key, flag and environment variable | docs/configuration.md |
| Look up a command or an exit code | docs/usage.md |
| Serve secrets to a sidecar, Vault Agent or the External Secrets Operator | docs/usage.md, docs/examples.md |
| See how the pieces fit | docs/architecture.md |
| Run an overlay host that carries a crypt | docs/host.md |
| Know the defaults and caps, what was measured, and what things cost | docs/limits.md |
| Implement a compatible owner, reader, auditor or host | docs/spec.md (the specification) |
| Know what cannot change once published | docs/frozen.md |
| Know what comes next | docs/roadmap.md |
$ (cd host && npm ci) # once: the TypeScript package's dependencies
$ make verify # gofmt, vet, dependency set, licenses, typecheck, vectors, tests
$ make bsecret # the command, bin/bsecret
$ make module # the host module, host/bundle/bsecret-module.js
$ make vectors # regenerate the golden vectors, both languages
$ make e2e REFERENCE_HOST=/path/to/host # the module on a local reference overlay host
$ make e2e-client REFERENCE_HOST=/path/to/host BORG_MODULE=/path/to/borg-module.js
# the command and borg's, against two local hosts loading both modules
$ make docker-build # the command's and the local chain's images
$ make quickstart-check # QUICKSTART.md, run as writtenGo 1.27.2 or later, and Node 24 for the TypeScript package under host/
(make verify NODE=/path/to/node24 when it is not on PATH). The direct Go
dependencies are go-sdk v1.7.1, bcommon v0.32.1 and borg v0.7.2, and nothing
else but bcommon's devkit v0.1.0, which only the tests and the devchain and
vectors commands import.
The record layer exists twice, held to one set of vectors: internal/secrec
in Go, and host/ in TypeScript. Each reads what the other writes
(testdata/vectors, testdata/ts). host/ is also the host module: the
topic manager tm_bsecret_<crypt> and the lookup service
ls_bsecret, bundled into one file an overlay host loads by path.
Apache 2.0; see LICENSE, NOTICE and LICENSE-THIRD-PARTY.