Skip to content

chore: dual licence (Apache-2.0 or MIT) + custody doc corrections#290

Merged
Jolah1 merged 4 commits into
mainfrom
chore/dual-licence-and-custody-docs
Jul 17, 2026
Merged

chore: dual licence (Apache-2.0 or MIT) + custody doc corrections#290
Jolah1 merged 4 commits into
mainfrom
chore/dual-licence-and-custody-docs

Conversation

@Jolah1

@Jolah1 Jolah1 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Follow-up to #289. That PR was squash-merged from an early snapshot of the branch, so these four commits never reached main. This lands the remainder.

1. Dual licence

main already advertised license = "MIT OR Apache-2.0" in every crate, but the repo only ever contained MIT. The second option had no terms behind it. So ship the text:

LICENSE.md        the explainer + contribution terms
LICENSE-MIT       (renamed from LICENSE; git tracks the rename, history preserved)
LICENSE-APACHE    canonical text, fetched verbatim from apache.org (11,358 bytes)
  • README + CONTRIBUTING name both licences and point at LICENSE.md
  • ghostkey-web/package.json had no licence field at all; now "MIT OR Apache-2.0"
  • Cargo.toml is untouched: it already said the right thing

CONTRIBUTING was the part that mattered. It told contributors their work was licensed "MIT or Apache-2.0 at the user's option" while the repo published only one of those.

Why both: Apache-2.0 carries an express patent grant that MIT lacks; MIT is GPLv2-compatible and Apache-2.0 isn't. Offering the pair lets a downstream project take whichever it needs, which matters for a codebase we want other wallets to borrow the inheritance pattern from.

2. Custody doc corrections

Both are the same root cause: docs not re-read since #124 killed the master-key heir derivation.

README listed, under "What works today", that the server derives a no-wallet heir's key from (master_key, heir_email, vault_id). That reads as current behaviour. It isn't: heir_derivation is null on every new vault, in both PasswordSetupPortal and AddHeirPortal. The claim is wrong in the worst direction, telling a reader that one master-key breach exposes every no-wallet heir key, which is the exact weakness #124 removed.

threat-model had the same staleness, and it overstated live risk: the F2 blast radius is closed for new vaults and does not grow.

The sharper find is D1. It says an operator "cannot construct a valid witness without either the owner's xprv or the heir's xprv", and never cross-references A3.2, where the master key derives the heir xprv for F2 vaults. Both statements are individually true, so the gap only shows if you read them together. D1 now says plainly that it does not hold for legacy F2 vaults, and notes the owner xprv is sealed under an Argon2id password KEK (verified: deriveOwnerKek runs Argon2id over the password, then HKDF, never the master key).

Verification

  • cargo check --workspace ✓ · cargo metadata reports MIT OR Apache-2.0 for all 7 crates ✓
  • package.json parses, licence field present ✓
  • no doc links the old ./LICENSE path ✓
  • Cargo.toml net diff vs main is empty, as intended ✓

🤖 Generated with Claude Code

Jolah1 and others added 4 commits July 15, 2026 07:31
README listed, under "What works today", that the server derives a
no-wallet heir's key from (master_key, heir_email, vault_id). That
reads as how the product works now. It isn't: #124 killed exactly that
backdoor, and heir_derivation is null on every new vault, in both
PasswordSetupPortal and AddHeirPortal.

The claim is the wrong way round to be wrong about. It tells a reader
(or an auditor) that one master-key breach exposes every no-wallet
heir key, which is the custody weakness the project deliberately
removed.

Describe what actually happens: the browser generates the heir key and
seals it under the one-time claim token, and the old derivation path
stays reachable only so pre-#124 vaults can still be claimed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same staleness the README had, but here it overstates live risk. The
doc presents "F2 server-derived flow" as a current heir-key mode and
A3.2 as a current blast radius. Since #124 no vault is created with
heir_derivation, so the exposed set is exactly the F2 vaults that
already existed, and it does not grow. An auditor reading this would
model a blast radius that no new vault has.

D1 was the sharper problem. It says an operator "cannot construct a
valid witness without either the owner's xprv or the heir's xprv",
and never cross-references A3.2, where the master key *derives* the
heir xprv for F2 vaults. Both statements are individually true, so
the gap only shows if you read them together. Now D1 says plainly
that it does not hold for legacy F2 vaults.

Also note the owner-xprv caveat under D1: an operator with the DB and
the master key still has to break an Argon2id password KEK to spend as
the owner (verified: deriveOwnerKek runs Argon2id over the password,
then HKDF, never the master key).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The repo claimed two different things. LICENSE and the README said
MIT; every crate said "MIT OR Apache-2.0", and no Apache-2.0 text
existed anywhere. So the crates offered users a licence whose terms
the project never shipped.

CONTRIBUTING was the part that actually mattered: it told every
contributor their work was licensed "MIT or Apache-2.0 at the user's
option". People have been agreeing to terms the project doesn't
publish.

Settle on MIT, which is what LICENSE, the README, and the public
framing have said all along:

- Cargo.toml -> license = "MIT" (all five inheriting crates follow)
- the two lightning crates declared it directly; updated too
- ghostkey-web/package.json had no license field; add "MIT"
- CONTRIBUTING points at LICENSE instead of naming a second licence

Worth knowing: MIT alone has no express patent grant, which is the
one thing Apache-2.0 would have added. Going MIT-only is the smaller
promise, and it's the one already being made.

Verified: cargo metadata reports MIT for every crate, cargo check and
npm run build pass, no "apache" reference left in the repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restores what the crates always claimed, and ships the text to back it
up. Before this, Cargo.toml offered users Apache-2.0 while the repo
contained only MIT, so the second option had no terms behind it.

- LICENSE -> LICENSE-MIT (git tracks the rename, history preserved)
- LICENSE-APACHE: the canonical text, fetched verbatim from
  apache.org rather than reproduced from memory (11,358 bytes)
- LICENSE.md: explains the choice, and states that contributions are
  dual licensed unless the contributor says otherwise
- Cargo.toml and the two lightning crates back to "MIT OR Apache-2.0"
- ghostkey-web/package.json likewise
- README and CONTRIBUTING name both licences and point at LICENSE.md

Why both rather than either alone: Apache-2.0 carries an express
patent grant, which MIT lacks, and MIT is GPLv2-compatible, which
Apache-2.0 is not. Offering the pair means a downstream project takes
whichever it needs, which matters for a codebase we want other wallets
to borrow the inheritance pattern from.

Verified: cargo metadata reports "MIT OR Apache-2.0" for every crate,
cargo check passes, package.json parses, and no doc still links the
old ./LICENSE path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ghost-key Ready Ready Preview, Comment Jul 15, 2026 6:32am

@Jolah1
Jolah1 merged commit 88c0762 into main Jul 17, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant