Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
073d6ca
Descriptor backup crypto: both on-chain formats, with conformance tests
benjamin-jarvie Sep 10, 2026
0f7a5ed
Correct the size framing: OP_RETURN has no cap at 600 bytes
benjamin-jarvie Sep 10, 2026
89f7e07
Pad BIP-138 backups to seven secret entries, and prove the lookup tag…
benjamin-jarvie Sep 11, 2026
8d1c6ab
Add the Descriptor Backup page
benjamin-jarvie Sep 11, 2026
2c5d06d
Service docs and a published vector for the descriptor backup
benjamin-jarvie Sep 11, 2026
dc859ad
Stop pointing clients at a competitor mid-session
benjamin-jarvie Sep 11, 2026
b16edda
The published vector is now a real transaction on mainnet
benjamin-jarvie Sep 11, 2026
06f50ec
Record the block and the pool for the published vector
benjamin-jarvie Sep 11, 2026
ed3290d
The free page now proves the backup landed, and writes the estate block
benjamin-jarvie Sep 11, 2026
63940ee
Fix seven findings from the code review of the descriptor page
benjamin-jarvie Sep 11, 2026
ca9eead
Guide screenshots from our own pages; dark viewfinder in every theme
benjamin-jarvie Sep 11, 2026
c89a2b0
Rename: the product is Bitcoin Inheritance; the code stays kaitiaki
benjamin-jarvie Sep 11, 2026
b02b26f
The old name goes from the code too: binary, keys, docs, comments
benjamin-jarvie Sep 11, 2026
d810ea7
Name the OP_RETURN output, and say what stays in plain text per format
benjamin-jarvie Sep 12, 2026
12bee48
Guide rewritten in our voice; the descriptor claim made precise; code…
benjamin-jarvie Sep 12, 2026
890fba4
Guide: eight facts corrected after review, and the remaining STE misses
benjamin-jarvie Sep 12, 2026
dbfe168
Pad BIP-138 entries to the buckets the standard asks for
benjamin-jarvie Sep 22, 2026
44f0402
README.pdf section headings wrap instead of clipping
benjamin-jarvie Sep 22, 2026
42704ab
Port the descriptor backup crypto to Go, both formats
benjamin-jarvie Sep 22, 2026
8b401a1
Stop blaming the key when a backup's contents cannot be read
benjamin-jarvie Sep 22, 2026
a7ead80
Carry the owner's three texts through the bundle
benjamin-jarvie Sep 22, 2026
8ac6bac
Maker: ask the owner what their heirs need to know
benjamin-jarvie Sep 22, 2026
3a85967
CLI: carry the owner's words, so a bundle matches the browser's
benjamin-jarvie Sep 23, 2026
c3a29d3
Landing page: show what each step of the drill looks like
benjamin-jarvie Sep 23, 2026
dc55d7b
Service docs: carry the owner's words, and state the fee assumption
benjamin-jarvie Sep 23, 2026
9e174e0
Guide: answer the argument against putting a backup on the chain
benjamin-jarvie Sep 23, 2026
56f559c
recover.html reads the chain copy, offline, with one key
benjamin-jarvie Sep 23, 2026
49064fc
recover.html names both ways in, and offers the second when the first…
benjamin-jarvie Sep 23, 2026
cf501cc
Annual drill: the guardian drives and the Butler stays silent
benjamin-jarvie Sep 23, 2026
3da8824
An empty bundle says it is empty
benjamin-jarvie Sep 23, 2026
d5bcdb1
The maker can put the owner's words on the chain
benjamin-jarvie Sep 23, 2026
8264f2f
descriptor.html becomes the heir's reading page
benjamin-jarvie Sep 23, 2026
f614103
The runbook says who pays the chain fee, and what the alert means
benjamin-jarvie Sep 23, 2026
b77a7cf
A try-it button on the descriptor page, and two friction fixes
benjamin-jarvie Sep 23, 2026
e402ca1
A test run in the maker, and the nudge to take it
benjamin-jarvie Sep 23, 2026
58204df
Review fixes: the demo flag, the TEST stamp, and the standards misses
benjamin-jarvie Sep 23, 2026
4319386
Say which copy an heir trusts, where they meet the second one
benjamin-jarvie Sep 23, 2026
e403197
The drill tells a stale chain copy from a broken one
benjamin-jarvie Sep 23, 2026
9722b3e
A revision session, and the retrieval list that makes it survivable
benjamin-jarvie Sep 23, 2026
362e9e2
A revision runs at a placement's length
benjamin-jarvie Sep 23, 2026
8c9b14f
Bring the guide and the README up to date with the bundles and the chain
benjamin-jarvie Sep 24, 2026
bf49ab9
The guide's figures photographed the wrong card
benjamin-jarvie Sep 24, 2026
fc72f68
Hold the guide to the pages it documents
benjamin-jarvie Sep 24, 2026
eb1efde
A guardian's bundle names its own holder and nobody else
benjamin-jarvie Sep 25, 2026
7b05165
The pages stop promising a contact list the bundles no longer carry
benjamin-jarvie Sep 25, 2026
970d9ee
A placement runs in the browser, end to end
benjamin-jarvie Sep 25, 2026
aba0a17
Self-hosting builds from this repository, not upstream's image
benjamin-jarvie Sep 25, 2026
a120adc
The upstream name leaves everything a user or a build touches
benjamin-jarvie Sep 25, 2026
09a5472
The last mention leaves the about page
benjamin-jarvie Sep 25, 2026
33180d0
Attribution trimmed to what the licence asks, and no further
benjamin-jarvie Sep 25, 2026
dabbf05
One source for the share format, and a README that is ours
benjamin-jarvie Sep 25, 2026
e0234b9
The guide gets pictures of the two things it could not show
benjamin-jarvie Sep 25, 2026
aca6d10
The printed page is ours, and the build instructions say one thing
benjamin-jarvie Sep 25, 2026
a8130ad
Say why the guardian list is safe in a will
benjamin-jarvie Sep 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.git
node_modules
dist
demo-recovery
demo-tlock
test-results
playwright-report
inheritance
inheritance-cjk
inheritance-data
*.zip
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ jobs:
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps

- name: Test descriptor backup formats
run: make test-ts

- name: Run Playwright E2E tests
run: make test-e2e

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
run: make build

- name: Generate HTML files
run: ./rememory html site -o _site
run: ./inheritance html site -o _site

- name: Add Google site verification file
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:
steps:
- uses: eljojo/no-autopilot@v1
with:
guidelines-url: 'https://github.com/eljojo/rememory/blob/main/AGENTS.md'
guidelines-url: 'https://github.com/Bitcoin-Butlers/kaitiaki/blob/main/AGENTS.md'
83 changes: 11 additions & 72 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ jobs:

- name: Generate standalone HTML files
run: |
./dist/rememory-linux-amd64 html create > dist/maker.html
./dist/rememory-linux-amd64 html recover > dist/recover.html
./dist/inheritance-linux-amd64 html create > dist/maker.html
./dist/inheritance-linux-amd64 html recover > dist/recover.html

- name: Generate demo bundles
run: |
./dist/rememory-linux-amd64 demo demo
./dist/inheritance-linux-amd64 demo demo
cd demo/output/bundles
zip -r ../../../dist/demo-bundles.zip *.zip

Expand All @@ -82,53 +82,6 @@ jobs:
cd dist
sha256sum * > checksums.txt

- name: Generate Homebrew formula
run: |
VERSION="${{ github.ref_name }}"
VERSION_NUM="${VERSION#v}"
SHA_DARWIN_ARM64=$(grep 'rememory-darwin-arm64$' dist/checksums.txt | awk '{print $1}')
SHA_DARWIN_AMD64=$(grep 'rememory-darwin-amd64$' dist/checksums.txt | awk '{print $1}')
SHA_LINUX_ARM64=$(grep 'rememory-linux-arm64$' dist/checksums.txt | awk '{print $1}')
SHA_LINUX_AMD64=$(grep 'rememory-linux-amd64$' dist/checksums.txt | awk '{print $1}')
cat > dist/rememory.rb << FORMULA
class Rememory < Formula
desc "A digital safe with multiple keys, held by people you trust"
homepage "https://github.com/eljojo/rememory"
version "${VERSION_NUM}"
license "Apache-2.0"

on_macos do
on_arm do
url "https://github.com/eljojo/rememory/releases/download/${VERSION}/rememory-darwin-arm64"
sha256 "${SHA_DARWIN_ARM64}"
end
on_intel do
url "https://github.com/eljojo/rememory/releases/download/${VERSION}/rememory-darwin-amd64"
sha256 "${SHA_DARWIN_AMD64}"
end
end

on_linux do
on_arm do
url "https://github.com/eljojo/rememory/releases/download/${VERSION}/rememory-linux-arm64"
sha256 "${SHA_LINUX_ARM64}"
end
on_intel do
url "https://github.com/eljojo/rememory/releases/download/${VERSION}/rememory-linux-amd64"
sha256 "${SHA_LINUX_AMD64}"
end
end

def install
bin.install Dir.glob("rememory-*").first => "rememory"
end

test do
assert_match version.to_s, shell_output("#{bin}/rememory --version")
end
end
FORMULA

- name: Upload dist artifacts
uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -168,9 +121,9 @@ jobs:
VERSION="${{ github.ref_name }}"

# Push architecture-specific images
docker tag rememory:latest "${IMAGE}:${VERSION}-amd64"
docker tag inheritance:latest "${IMAGE}:${VERSION}-amd64"
docker push "${IMAGE}:${VERSION}-amd64"
docker tag rememory:latest-arm64 "${IMAGE}:${VERSION}-arm64"
docker tag inheritance:latest-arm64 "${IMAGE}:${VERSION}-arm64"
docker push "${IMAGE}:${VERSION}-arm64"

# Create and push multi-arch manifests
Expand Down Expand Up @@ -202,32 +155,18 @@ jobs:
run: |
# Create draft release with assets (drafts can be modified)
gh release create ${{ github.ref_name }} \
dist/rememory-linux-amd64 \
dist/rememory-linux-arm64 \
dist/rememory-darwin-amd64 \
dist/rememory-darwin-arm64 \
dist/rememory-windows-amd64.exe \
dist/inheritance-linux-amd64 \
dist/inheritance-linux-arm64 \
dist/inheritance-darwin-amd64 \
dist/inheritance-darwin-arm64 \
dist/inheritance-windows-amd64.exe \
dist/demo-bundles.zip \
dist/checksums.txt \
dist/maker.html \
dist/recover.html \
dist/rememory.rb \
--generate-notes \
--notes "**You can use ReMemory without installing anything.** Download \`maker.html\` from the assets below, save it to your desktop, and open it in any browser to create bundles." \
--notes "**You can use Bitcoin Inheritance without installing anything.** Download \`maker.html\` from the assets below, save it to your desktop, and open it in any browser to create bundles." \
--draft
# Publish the release (now it becomes immutable)
gh release edit ${{ github.ref_name }} --draft=false

- name: Update Homebrew tap
env:
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
git clone https://x-access-token:${TAP_TOKEN}@github.com/eljojo/homebrew-rememory.git /tmp/tap
mkdir -p /tmp/tap/Formula
cp dist/rememory.rb /tmp/tap/Formula/rememory.rb
cd /tmp/tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/rememory.rb
git commit -m "Update rememory to ${{ github.ref_name }}"
git push
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/internal/html/assets/tlock-recover.js
/internal/html/assets/app-selfhosted.js
/internal/html/assets/create-app-selfhosted.js
/internal/html/assets/descriptor-app.js
/internal/html/assets/app-tlock.js
/node_modules/
/e2e/playwright-report/
Expand All @@ -22,5 +23,6 @@
/maker.html
.claude
/rememory-data
/kaitiaki
/kaitiaki-cjk
/inheritance
/inheritance-cjk
/.test-build/
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

This file provides guidance for contributors and coding agents in this repository.

## What is Kaitiaki
## What is Bitcoin Inheritance

Kaitiaki encrypts files with [age](https://github.com/FiloSottile/age), splits the decryption key among trusted friends using Shamir's Secret Sharing (via HashiCorp Vault's implementation), and gives each friend a self-contained offline recovery tool (`recover.html`) that works in any browser without servers or internet.
Bitcoin Inheritance encrypts files with [age](https://github.com/FiloSottile/age), splits the decryption key among trusted friends using Shamir's Secret Sharing (via HashiCorp Vault's implementation), and gives each friend a self-contained offline recovery tool (`recover.html`) that works in any browser without servers or internet.

## Ownership Mindset

Expand Down Expand Up @@ -144,7 +144,7 @@ For `recover.html`, no WASM is needed — all crypto is native JavaScript bundle

### Bundle generation

Each friend's ZIP bundle contains: `README.txt`, `README.pdf`, `MANIFEST.age`, a personalized `recover.html` (with their share pre-loaded and contact list embedded), and `OWNER.age` when the creator supplied an owner key (the passphrase encrypted to their age recipient — see `internal/core/owner.go`). Generated by `internal/bundle/`.
Each friend's ZIP bundle contains: `README.txt`, `README.pdf`, `MANIFEST.age`, a personalized `recover.html` (with their share pre-loaded), and `OWNER.age` when the creator supplied an owner key (the passphrase encrypted to their age recipient — see `internal/core/owner.go`). Generated by `internal/bundle/`.

The owner key is currently browser-only: the maker creates `OWNER.age` and `recover.html` accepts the owner identity, but `cmd/rememory` has no owner-key flag on create or recover. CLI recovery works with a stock `age` binary (see `docs/independent-recovery.md`). Adding CLI flags is a known, deliberate gap.

Expand Down Expand Up @@ -237,7 +237,7 @@ See `CONTRIBUTING.md` for the full contribution guidelines, including the AI usa

### Changelog

`CHANGELOG.md` entries should focus on **what changed for the person using Kaitiaki**, not on implementation details. Lead with the user-facing outcome, then explain just enough context for it to make sense.
`CHANGELOG.md` entries should focus on **what changed for the person using Bitcoin Inheritance**, not on implementation details. Lead with the user-facing outcome, then explain just enough context for it to make sense.

- **Good:** "Encrypted archives up to 10 MB are now embedded directly in `recover.html`. More people will be able to recover by just opening the HTML file."
- **Bad:** "Raised `MaxEmbeddedManifestSize` from 5 MB to 10 MB."
Expand Down
84 changes: 83 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,80 @@
# Changelog

All notable changes to ReMemory are documented here.
## Unreleased

- Sealing no longer writes the owner's own words into their project folder.
They are built in memory and handed straight to the archive. Before this, an
owner who edited `manifest/HOW-THE-WALLET-WORKS.txt` by hand lost the edit on
the next `seal`, and their words sat in plaintext on disk afterwards.

- The guide, the README, the CLI guide, the security review and the three
service runbooks now describe the tool as it is. Twenty places said a bundle
lists the other guardians, that the owner's words sit in the open, or that
the chain copy rides in every README. A new section, **Who Can Read What**,
states the three tiers of access in one table, so there is one place to check
a claim against.
- **Publish the chain copy before you generate, and paste its transaction id
into the page.** A new field sits under the descriptor. This is the only way
the id reaches your guardians: the archive is encrypted and its key split
among them the moment you generate, so an id found afterwards can never be
added. Write it on your estate page as well. The guide and the placement
runbook used to say a missing id cost an heir nothing, because the bundles
carried the chain copy in the open. They no longer do.
- The guide's screenshots are regenerated. They showed the Named and Anonymous
tabs, and a contact list that no longer exists.

- Anonymous mode is gone. It existed so an owner could keep guardians from
seeing each other's names, and every bundle now does that by default. What
was left of it was a second way to do the same thing, with its own tab, its
own share-count field, its own CLI flags and its own recovery instructions.
Guardians are named in the maker, and their names go nowhere but their own
bundle. `inheritance init` loses `--anonymous` and `--shares`.

- A guardian's bundle no longer tells them who the other guardians are. The
`README.txt`, the `README.pdf` and the personalised `recover.html` listed
every other guardian by name and email, so one bundle in the wrong hands
named the rest of the people to approach. All three surfaces now name their
own holder and nobody else. Who holds a piece belongs in the owner's will or
their estate insert, where estate practice already keeps it.
- Everything the owner writes is now sealed inside the encrypted archive, so it
opens only when enough guardians combine their pieces. Their method for
spending and the encrypted chain copy used to sit in the open beside the
roster, where a single guardian read both. They now arrive as
`HOW-THE-WALLET-WORKS.txt` and `CHAIN-COPY.txt`, next to
`WHERE-THE-KEYS-ARE.txt`. The published transaction id travels with the
chain copy, because the id alone fetches the same payload off the chain.
A bundle README says only whether the owner wrote anything at all.
- The `--chain-txid` flag on `bundle` is gone. Bundles no longer carry a
transaction id, so there was nothing for it to set.

- The descriptor page can open our own published backup. One button fills the
transaction id and two of the three keys from the mainnet backup recorded in
`docs/descriptor-backup-vector.md`, fetches it, and leaves the reader one
press from the descriptor. It stops there on purpose, because the
transaction-id path takes two steps and nothing used to say so. The page now
says so, and pressing Rebuild too early names the Fetch button instead of
restating the problem. The note explaining what happened appears only for our
own backup, so an heir recovering their own wallet is never told it was a
demonstration.
- The maker offers a test run. It makes throwaway bundles from a sample file so
an owner can practise a recovery before making the real ones. A test bundle
carries none of the owner's own material, neither their files nor the people
and places, and every one is stamped TEST in the project name and in the
saved filename. The offer appears once, never repeats, and never blocks
Generate. Covered by `e2e/test-run.spec.ts`.

- Kaitiaki is now Bitcoin Inheritance. Pages, guide, README, the PDF bundle
title, and every translation say the new name; short labels such as the page
logo say Inheritance. The CLI binary is now `inheritance` (`inheritance init`,
`inheritance seal`, `inheritance recover`), the bundle metadata key is
`inheritance-version`, and the printed recovery URL moved to
bitcoinbutlers.com/tools/inheritance/recover.html, which the site redirects
to permanently from the old path. Only the repository name keeps the old word.

All notable changes to Bitcoin Inheritance are documented here. Entries below
the 2026 fork describe releases of ReMemory, the project this one is built
from, and name its binary and commands as they were at the time. They are a
record, not instructions.

## Unreleased

Expand All @@ -19,6 +93,14 @@ All notable changes to ReMemory are documented here.
active label used to be white on white.
- The "See how it works" walkthrough now builds a test set in the maker
instead of pointing at a demo download this fork does not publish.
- New page: **Descriptor Backup**. A multisig wallet needs its descriptor as
well as its keys. Losing the descriptor together with one key can cost you
the wallet, even when the keys you still hold are enough to sign. The page encrypts the descriptor so that your own
keys unlock it, and gives you one line of text to put on Bitcoin. Choose
who can open it: the same threshold your wallet spends with, or any single
key. Recovery reads it back from a transaction id or from pasted text.
Nothing is uploaded, and the encrypted text can also be recovered with
other people's tools if this project disappears.
- The guardian README.txt and README.pdf speak for Bitcoin Butlers. The
title is now KAITIAKI GUARDIAN BUNDLE, the instructions are shorter and
plainer, and the fallback links point at bitcoinbutlers.com and at this
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to Kaitiaki
# Contributing to Bitcoin Inheritance

Kaitiaki is a project where quality matters more than usual. A recovery bundle might sit in a drawer for ten years, then be opened by someone who just lost a loved one. The code, the copy, the design — it all has to hold up. Contributions should reflect that care.
Bitcoin Inheritance is a project where quality matters more than usual. A recovery bundle might sit in a drawer for ten years, then be opened by someone who just lost a loved one. The code, the copy, the design — it all has to hold up. Contributions should reflect that care.

We welcome contributions. Here's how to make them count.

Expand Down
40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Bitcoin Inheritance, built from this repository.
#
# The build needs Node as well as Go: the pages are TypeScript compiled by
# esbuild, and the maker's create.wasm is Go compiled for js/wasm.

FROM golang:1.25-bookworm AS build

RUN apt-get update \
&& apt-get install -y --no-install-recommends nodejs npm \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /src

# Dependencies first, so a source edit does not refetch them.
COPY go.mod go.sum ./
RUN go mod download
COPY package.json package-lock.json ./
RUN npm ci

COPY . .
ENV PATH="/src/node_modules/.bin:${PATH}"
RUN make build

FROM debian:bookworm-slim

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --create-home --uid 10001 inheritance

COPY --from=build /src/inheritance /usr/local/bin/inheritance

USER inheritance
WORKDIR /data
VOLUME ["/data"]
EXPOSE 8080

# 0.0.0.0 because the default 127.0.0.1 is unreachable from outside the
# container. Put it behind a reverse proxy with TLS.
ENTRYPOINT ["inheritance", "serve", "--host", "0.0.0.0", "--port", "8080", "--data", "/data"]
Loading
Loading