SS
← All projectsSeedSigner

SeedSigner

Stateless, air-gapped Bitcoin signing software for Raspberry Pi hardware.

BitcoinHardware walletsNormal
Repository coverage

229 commits in the local evidence base

Every captured commit receives deterministic security triage and a separate communication-quality score. Security candidates and broader second-pass signals receive full-patch Ollama analysis.

168security candidates28second-pass queue223AI analyses
14commits · 30 days
40commits · 60 days
60commits · 180 days
204commits · 365 days
Backfill bands
Aug 5 → Feb 6150 seen8 candidatesComplete
Feb 6 → Jun 629 seen0 candidatesComplete
Jun 6 → Jul 60 seen0 candidatesComplete
Jul 6 → Aug 57 seen1 candidatesComplete
Commit communication

Does the history explain itself?

Message quality measures whether a commit identifies its scope, purpose, rationale, testing, and supporting references. It does not change the security-severity score.

49/100 average clarity
15Strong · 80–100
56Adequate · 60–79
92Thin · 40–59
66Opaque · 0–39
44security candidates with opaque commit messaging
Read the scoring rubric →
Developer activity

Who is changing the project?

Public Git author strings; identities are not independently verified.

DeveloperCommitsCandidatesAnalyzedHigh riskMessage avg.
Nick Klockenga282324268
kdmukai141100141145
okaybro191619064
PROWLERx15101010020
alvroble12711055
FazleRabbbiferdaus172868061
biel414037
securesigner111050
Chaitanya-Keyal111057
Kshitij111033
Advait111060
S1DDHEY111045
Analysis record

Published AI watches

Last scanned 53 minutes ago

Informational 15 AI analysisMessage 73 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Merge pull request #722 from Chaitanya-Keyal/psbt-huge-fee-warning

This commit adds a new user-facing safety feature: a warning screen when a Bitcoin transaction's fee is unusually high compared to the amount being sent. It does not fix a bug or vulnerability; it helps users notice potentially costly mist…

New user-facing warning for high transaction feesThreshold-based fee check computed from PSBT outputsVisual warning mark and color change on fee display
088b144eby Nick Klockenga+238−55 files
No security note in commit
Informational 20 AI analysisMessage 68 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Settle is_high_fee once in parse()

This commit is a performance cleanup, not a security fix. It calculates the 'is this fee unusually high?' flag once during transaction parsing instead of recalculating it every time a screen asks. The actual fee-checking logic and warning …

No security-relevant logic change: the same fee threshold and calculation are usedPerformance optimization only: eliminates repeated transaction re-summationNo input validation, cryptography, or authorization changes
2195adfdby okaybro+32−53 files
No security note in commit
High 80 AI analysisMessage 88 · Strong
SS SeedSignerSeedSigner BitcoinHardware wallets

Merge pull request #1032 from kdmukai/psbt_output_ownership

This commit fixes a security flaw in SeedSigner, a small hardware-wallet-like device used to sign Bitcoin transactions. Previously, the software trusted the PSBT file's own claim about which 'change' outputs belong back to the user's walle…

Fixes change-output ownership verification against PSBT coordinator claimsAdds independent scriptPubKey reconstruction from seed-derived keysAdds rejection of contradictory ownership claims (claim-ours/pay-other and pay-ours/claim-other)
b225ae77by Nick Klockenga+1756−2636 files
Vendor flagged security relevance
Moderate 63 AI analysisMessage 83 · Strong
SS SeedSignerSeedSigner BitcoinHardware wallets

Count an unresolved output cosigner set as a mismatch

This commit fixes a bug in SeedSigner's PSBT parser that could misclassify a payment to a different multisig wallet as 'change' (money going back to the user's own wallet). The bug occurred when the output's cosigner list could not be reso…

Change-vs-spend misclassification in multisig PSBT parsingCosigner resolution failure previously skipped instead of treated as mismatchFingerprint matching removed in favor of derivation-path + derived-key verification
45a5eabbby kdmukai+102−462 files
Vendor flagged security relevance
Low 34 AI analysisMessage 100 · Strong
SS SeedSignerSeedSigner BitcoinHardware wallets

Compare cosigners before presuming multisig change

This commit tightens how SeedSigner decides whether a multisig payment back to the user is real 'change' or actually a payment to a different wallet. Before, if an output contained the user's key and looked like the same kind of multisig (…

Multisig change-output presumption logic changed to compare input vs output cosigner listsOptional/unauthenticated PSBT global xpubs used only as a hygiene check, not a hard security controlMismatch demotes output from change to spend rather than raising an error
ff133966by kdmukai+244−62 files
Vendor flagged security relevance
Informational 15 AI analysisMessage 58 · Thin
SS SeedSignerSeedSigner BitcoinHardware wallets

Rename is_change to is_presumed_change

This commit only renames a variable from 'is_change' to 'is_presumed_change' and updates comments/docstrings to make it clearer that multisig outputs are only suspected—not proven—to belong to the user's wallet. The code logic is unchanged…

No functional code changeVariable rename and documentation clarification onlyCommit message explicitly states 'No behavior change'
321bf14dby kdmukai+19−131 file
No security note in commit
Moderate 60 AI analysisMessage 83 · Strong
SS SeedSignerSeedSigner BitcoinHardware wallets

Match policy types exactly and refuse an unsupported one

This commit fixes a bug in how SeedSigner parses Bitcoin transaction outputs. Previously, the code guessed script types using partial string matches (like checking if 'pkh' appears anywhere in the type name) and had no fallback for unknown…

CWE-20: Improper Input Validation - substring matching allowed ambiguous/unsupported script type handlingCWE-248: Uncaught Exception - unsupported script types caused bare TypeError instead of controlled failureCWE-754: Improper Check for Unusual or Exceptional Conditions - missing else branch for unsupported policy types
a929b8b3by kdmukai+38−112 files
No security note in commit
Moderate 59 AI analysisMessage 85 · Strong
SS SeedSignerSeedSigner BitcoinHardware wallets

Set is_change only after each branch's own checks

This commit fixes a logic bug in how SeedSigner decides whether a Bitcoin transaction output is 'change' coming back to the user's wallet. Previously, the code marked an output as change as soon as it looked like it paid to the user's seed…

Logic bug: default-true flag inside a conditional arm with branches responsible for undoing itPotential UI deception: unverified output could be displayed as user changeDefensive refactor: flag set only after branch-specific verification
12c72391by kdmukai+23−71 file
Vendor flagged security relevance
High 79 AI analysisMessage 78 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Verify that change outputs actually pay this seed

This commit fixes a security flaw in how SeedSigner decides whether a Bitcoin transaction's 'change' output really returns coins to the user's own wallet. Previously, the device trusted too much of what the transaction coordinator (or a ma…

Fixes insufficient verification of change output ownership in PSBT parsingAdds explicit rejection of ownership-claim/scriptPubKey contradictions treated as attacksAdds rejection of surplus derivation path entries and mixed ecdsa/taproot derivation maps
bb2471a6by kdmukai+1390−2406 files
Vendor flagged security relevance
Informational 15 AI analysisMessage 68 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Clarify psbt_parser.py docstrings and comments

This commit only rewrites comments and docstrings in one file and renames a single function parameter for clarity. The commit message explicitly says 'No behavior change,' and the diff contains no changes to executable logic, control flow,…

0d374bc7by kdmukai+28−371 file
No security note in commit
High 78 AI analysisMessage 78 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Merge pull request #1013 from kdmukai/psbt_parser_ownership_scan

This commit hardens SeedSigner's PSBT (Bitcoin transaction) parser so it no longer trusts a transaction's claim that an output belongs to the user's wallet. Previously, a malicious or buggy transaction could mark a payment to an attacker's…

Fixes fake-change / ownership-spoofing vulnerability in PSBT parsingAdds explicit rejection of false seed-ownership claims on outputs and inputsAdds user-facing warning screens for forged ownership claims
e4af6a7eby Nick Klockenga+994−516 files
Vendor flagged security relevance
Informational 15 AI analysisMessage 73 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Merge pull request #1005 from kdmukai/psbt_parser_trust_vocabulary

This commit is a code cleanup that renames internal data fields from 'fingerprint' and 'derivation_path' to 'claimed_fingerprints' and 'claimed_derivation_paths'. It does not change how the software verifies Bitcoin transaction data or how…

No functional security change; only identifier renaming and documentationExpanded docstring explicitly distinguishes claimed_ vs verified_ dataNo new validation, no removed validation, no algorithm change
d70b322fby Nick Klockenga+62−383 files
No security note in commit
Informational 15 AI analysisMessage 28 · Opaque
SS SeedSignerSeedSigner BitcoinHardware wallets

Improve comment accuracy

This commit only rewrites comments and docstrings in a single Python file to make them more accurate. No code logic, behavior, or security properties were changed.

c7eacfbdby kdmukai+18−141 file
No security note in commit
Informational 15 AI analysisMessage 68 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Pluralize change_data's claimed keys

This commit simply renames two internal data keys from singular to plural (e.g., 'claimed_fingerprint' becomes 'claimed_fingerprints') because they actually hold lists of values. It also removes a commented-out debug print line. There is n…

4f00d32fby kdmukai+14−153 files
No security note in commit
Informational 15 AI analysisMessage 28 · Opaque
SS SeedSignerSeedSigner BitcoinHardware wallets

Improve comment accuracy

This commit only changes a comment in the source code to make it more accurate. It does not modify any actual code behavior, so it has no security impact on its own.

622a9fedby kdmukai+2−11 file
No security note in commit
Informational 19 AI analysisMessage 73 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Merge pull request #1002 from kdmukai/psbt_parser_derivation_cache

This commit is a performance improvement for parsing Bitcoin transaction files (PSBTs). It adds a cache so the wallet doesn't repeatedly recalculate the same cryptographic key derivations, and it avoids rebuilding the whole transaction obj…

Removed p2sh redeem-script handling path in change detectionNew derivation cache keyed by id(parent_key) with explicit rejection of fingerprint-based keying due to collision riskFingerprint recovery now uses root.my_fingerprint instead of root.child(0).fingerprint
861d6e3cby Nick Klockenga+419−352 files
No security note in commit
Informational 15 AI analysisMessage 68 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Mark coordinator-supplied change data as claimed

This commit is a code cleanup and documentation change, not a security fix. It renames variables and dictionary keys from 'fingerprint' and 'derivation_path' to 'claimed_fingerprint' and 'claimed_derivation_path' to make it clearer that da…

Renames unverified coordinator-supplied metadata keys to 'claimed_' prefixAdds trust-boundary documentation and naming convention in class docstringExplicitly states no logic changes in commit message
fc21710bby kdmukai+47−273 files
No security note in commit
Informational 15 AI analysisMessage 78 · Adequate
SS SeedSignerSeedSigner BitcoinHardware wallets

Tighten the derivation cache guard tests

This commit only changes test code. It tightens an existing unit test so it actually exercises the cache read path, and improves comments and docstrings. No production code was modified, so it cannot introduce or fix a security vulnerabili…

65b1d3d9by kdmukai+22−131 file
No security note in commit
Repository ledger

Explore captured commits

Expand any commit for its author, full message, clarity score, changed files, triage signals, analysis, and source link.

Security candidateMerge pull request #722 from Chaitanya-Keyal/psbt-huge-fee-warningby Nick Klockenga · 088b144e · Sep 23, 2026 · 5 filesMessage 73 · AdequateInformational 15Details
Commit message · Nick Klockenga

Merge pull request #722 from Chaitanya-Keyal/psbt-huge-fee-warning

[Feature] Warning Screen for High Tx Fees

73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body✓ Links an issue, advisory, or supporting reference
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol pathmerge-commit duplicate discount
AI analysis · Informational 15/100

This commit adds a new user-facing safety feature: a warning screen when a Bitcoin transaction's fee is unusually high compared to the amount being sent. It does not fix a bug or vulnerability; it helps users notice potentially costly mistakes before signing.

Security candidateSettle is_high_fee once in parse()by okaybro · 2195adfd · Sep 20, 2026 · 3 filesMessage 68 · AdequateInformational 20Details
Commit message · okaybro

Settle is_high_fee once in parse()

Both PSBTOverviewView and PSBTMathView asked the parser whether the fee was
high, and the overview asked twice (once for the screen, once for routing).
Each call re-summed the outputs through psbt.tx, which rebuilds the whole
transaction on every access. The totals it depends on are all fixed by the end
of parse(), so compute it there once and store it as is_high_fee, the same way
the other totals are exposed. has_high_fee() stays as the computation itself.

68/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 20/100

This commit is a performance cleanup, not a security fix. It calculates the 'is this fee unusually high?' flag once during transaction parsing instead of recalculating it every time a screen asks. The actual fee-checking logic and warning behavior are unchanged, so users still see the same high-fee warnings. The change just makes the device respond faster and avoids redundant work.

Security candidateMerge pull request #1032 from kdmukai/psbt_output_ownershipby Nick Klockenga · b225ae77 · Sep 19, 2026 · 6 filesMessage 88 · StrongHigh 80Details
Commit message · Nick Klockenga

Merge pull request #1032 from kdmukai/psbt_output_ownership

[security] Verify that change outputs actually pay this seed

88/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body✓ Mentions testing or verification✓ Links an issue, advisory, or supporting reference✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol pathmerge-commit duplicate discount
AI analysis · High 80/100

This commit fixes a security flaw in SeedSigner, a small hardware-wallet-like device used to sign Bitcoin transactions. Previously, the software trusted the PSBT file's own claim about which 'change' outputs belong back to the user's wallet. A malicious or buggy PSBT could mark an external payment as the user's own change, tricking the user into approving a transaction that sends funds to an attacker. The patch now independently rebuilds each output's receiving address from the user's seed and compares it to what the PSBT actually commits to, rejecting the transaction if the two disagree. It also adds checks for contradictory or malformed derivation-path claims.

Security candidateCount an unresolved output cosigner set as a mismatchby kdmukai · 45a5eabb · Sep 16, 2026 · 2 filesMessage 83 · StrongModerate 63Details
Commit message · kdmukai

Count an unresolved output cosigner set as a mismatch

The cosigner comparison skipped any output whose cosigners failed to
resolve, so the different-quorum case it exists for never fired on a
real psbt: coordinators write only the spending wallet's xpubs, and a
foreign cosigner's key then resolves against nothing. Once the inputs'
cosigners resolve, an output that differs or fails to resolve is
counted as a spend.

Keys now match xpubs by derivation alone rather than requiring the
claimed fingerprint to match first. The fingerprint is a label the
coordinator wrote; requiring it let one mislabeled cosigner turn
genuine change into a spend, and a key an xpub really derives is
proof enough on its own.

The different-quorum test now uses the wallet's own xpubs, the shape
every xpub-writing coordinator produces, and a second test keeps the
four-xpub variant.

83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Moderate 63/100

This commit fixes a bug in SeedSigner's PSBT parser that could misclassify a payment to a different multisig wallet as 'change' (money going back to the user's own wallet). The bug occurred when the output's cosigner list could not be resolved, because the comparison was skipped entirely. The fix now counts an unresolved or mismatched cosigner set as a spend instead of change. It also changes how keys are matched to xpubs: instead of first requiring the coordinator-provided fingerprint to match, it now trusts a matching derivation path and a proven derived key. This prevents a mislabeled fingerprint from turning a real change output into what looks like a spend.

Security candidateCompare cosigners before presuming multisig changeby kdmukai · ff133966 · Sep 12, 2026 · 2 filesMessage 100 · StrongLow 34Details
Commit message · kdmukai

Compare cosigners before presuming multisig change

A multisig output whose committed script holds this seed's key can
still belong to a different wallet: a 2-of-3 over {p1,p2,p4} when the
inputs spend {p1,p2,p3}. When the psbt supplies global xpubs and fully
annotates the output, _get_cosigners resolves a cosigner list for the
inputs and one for the output, and lists that differ mean the output
pays a quorum other than the one being spent from. That output is now
counted as an external spend. It stays presumed change when the lists
match or when either side's cosigners are unresolved.

This is hygiene, not a security control. The global xpubs are optional
and unauthenticated, so a coordinator that omits them skips the check,
and a mismatch demotes rather than raises because a transfer into a
second multisig this seed belongs to is legitimate. No surveyed
coordinator produces the shape the check fires on: every one except
Bitcoin Core annotates only its own wallet's outputs, and Core, which
annotates any output a descriptor in its wallet file can solve, writes
no global xpubs at all. Settling which wallet a multisig output pays
still needs the user's own descriptor.

The comparison sits after the ownership checks rather than at the
policy shape gate, so a psbt whose output cosigners fail to resolve
still faces every ownership check.

_get_cosigners' sorted() is what makes the two lists comparable: two of
the three multisig fixtures order the same wallet's keys differently on
the input and on its change output. Its docstring now separates what
is claim from what is checked and what the result does and does not
establish, its loop carries the procedure inline, and two early-outs
name the omitted-xpubs and omitted-derivations cases before the loop
(behaviour-neutral: both already raised from inside it). Tests pin the
sort, the different-quorum spend, and the no-xpubs presumed-change
fallback.

100/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryaccess controlseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Low 34/100

This commit tightens how SeedSigner decides whether a multisig payment back to the user is real 'change' or actually a payment to a different wallet. Before, if an output contained the user's key and looked like the same kind of multisig (for example, 2-of-3), the app assumed it was change. Now, when the PSBT file includes enough extra public-key data, the app compares the list of cosigners on the inputs versus the output. If the cosigner lists differ, the output is treated as an external spend instead of change. The commit itself calls this 'hygiene, not a security control' because the extra data is optional and unauthenticated, so a malicious or uncooperative coordinator can still bypass the check by omitting it.

Security candidateRename is_change to is_presumed_changeby kdmukai · 321bf14d · Sep 11, 2026 · 1 fileMessage 58 · ThinInformational 15Details
Commit message · kdmukai

Rename is_change to is_presumed_change

The flag in _parse_outputs is set for two different strengths of
finding. For single sig our own key rebuilt the scriptPubKey, so the
output is provably ours. For multisig we have only shown that our key
sits in the committed script, and which wallet that script belongs to
is settled later, by the user's descriptor. Calling both "is_change"
gave the multisig case a certainty it does not have.

The process docstring is held to the same standard: it describes how
ownership is determined rather than how outputs are verified as change,
the verdict table reads "presumed change", and the multisig rebuild
bullet names the key-in-script step that the table row depends on.

No behavior change.

58/100 · ThinMessage clarity
✓ Descriptive subject✓ Provides detailed explanatory context
Why it was queued
seed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit only renames a variable from 'is_change' to 'is_presumed_change' and updates comments/docstrings to make it clearer that multisig outputs are only suspected—not proven—to belong to the user's wallet. The code logic is unchanged, so there is no security fix or behavior change.

Security candidateMatch policy types exactly and refuse an unsupported oneby kdmukai · a929b8b3 · Sep 11, 2026 · 2 filesMessage 83 · StrongModerate 60Details
Commit message · kdmukai

Match policy types exactly and refuse an unsupported one

The rebuild chain in _parse_outputs matched single sig and taproot by
substring ("pkh" in type, "p2tr" in type) and had no else. embit reports
a script type it does not recognize as None, and _get_policy passes that
through, so a psbt whose inputs and outputs were both unrecognized passed
the shape gate and died in the substring test with a bare TypeError. The
guard meant for an unhandled type sat inside the scriptPubKey match,
where an empty rebuild cannot arrive.

The arms now name their types exactly, matching _build_singlesig_script,
and the chain ends in a catch-all else that raises RuntimeError with the
unsupported type in the message. The unreachable inner guard is removed.
A test drives a p2pk psbt into the catch-all.

83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Moderate 60/100

This commit fixes a bug in how SeedSigner parses Bitcoin transaction outputs. Previously, the code guessed script types using partial string matches (like checking if 'pkh' appears anywhere in the type name) and had no fallback for unknown types. A specially crafted transaction using an unsupported script type could slip past the safety checks and crash the device with a confusing error, or potentially be misclassified as change. The patch now matches script types exactly and explicitly rejects any unsupported type with a clear error message.

Security candidateSet is_change only after each branch's own checksby kdmukai · 12c72391 · Sep 11, 2026 · 1 fileMessage 85 · StrongModerate 59Details
Commit message · kdmukai

Set is_change only after each branch's own checks

In _parse_outputs the flag was set True as soon as the rebuilt
scriptPubKey matched, and two multisig branches then turned it back
off. That made "change" the default inside the matched arm, so a branch
that forgot the flag would record change with nothing verified behind
it. Now each branch sets the flag only once its own checks have passed,
and an output that satisfies none of them stays an external spend.

The two sites where the flag is set split by signature type, and each
carries the certainty it actually has: single sig is settled, because
our own key rebuilt the scriptPubKey; multisig is a presumption, because
our key in the committed script does not say which wallet that script
belongs to, and only the user's descriptor can. The reminder that
"change" means any output coming back to our seed moves to the line
where change_data is built, which is where the word takes that meaning.

No behavior change: the same outputs land in the same lists on every
fixture shape, and the suite is unchanged.

85/100 · StrongMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Moderate 59/100

This commit fixes a logic bug in how SeedSigner decides whether a Bitcoin transaction output is 'change' coming back to the user's wallet. Previously, the code marked an output as change as soon as it looked like it paid to the user's seed, before fully checking the details. Two special cases (multisig wallets) then had to manually undo that mark. The danger was that any future code path that forgot to undo the mark could wrongly label an external payment as the user's own change, potentially tricking a user into approving a transaction that sends funds to an attacker. The patch moves the 'is_change = True' assignment so it only happens after each branch's specific safety checks have passed. The commit itself says there is no behavior change on current test fixtures, but it removes a risky default.

Security candidateVerify that change outputs actually pay this seedby kdmukai · bb2471a6 · Sep 5, 2026 · 6 filesMessage 78 · AdequateHigh 79Details
Commit message · kdmukai

Verify that change outputs actually pay this seed

An output counted as change when its policy matched the inputs' and its
rebuilt scriptPubKey matched what the output committed to. Neither step
established that the key involved was ours. The policy comparison
included cosigners resolved from the coordinator's own global xpubs, so
one misannotated fingerprint made an output stop matching and skip
verification altogether, and multisig never checked our key against the
committed script at all.

Outputs now compare on script shape alone, and every candidate proves
ownership: single sig by rebuilding from the claimed derivation path,
multisig by finding this seed's key in the committed script. Where the
psbt's account of an output contradicts what the output commits to, the
parse refuses rather than quietly reclassifying. A claim set too
malformed to answer that question, one populating both derivation path
maps or claiming more keys than its script uses, is refused as well.

change_data now carries the verified derivation path in place of the
coordinator's claimed fingerprints and paths, so the views no longer
re-derive trust from strings the parse has already settled.

Taproot mismatches stay exempt. A script tree tweaks the internal key,
so honest taproot change fails the rebuild too, and embit leaves
PSBT_OUT_TAP_TREE unparsed, which is what would tell the two apart.

78/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundarydefensive validationseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · High 79/100

This commit fixes a security flaw in how SeedSigner decides whether a Bitcoin transaction's 'change' output really returns coins to the user's own wallet. Previously, the device trusted too much of what the transaction coordinator (or a malicious wallet app) claimed about output ownership, which could let an attacker trick the user into approving a payment that actually sends change to the attacker. The fix makes the device independently prove ownership of each change output using the seed's own keys, and it now rejects several kinds of contradictory or misleading PSBT annotations as attacks.

Security candidateMerge pull request #1027 from kdmukai/psbt_parser_docstringsby Nick Klockenga · 85cd9a02 · Sep 4, 2026 · 1 fileMessage 73 · AdequateInformational 15Details
Commit message · Nick Klockenga

Merge pull request #1027 from kdmukai/psbt_parser_docstrings

Clarify psbt_parser.py docstrings and comments; minor cleanup

73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body✓ Links an issue, advisory, or supporting reference
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol pathmerge-commit duplicate discount
AI analysis · Informational 15/100

This commit only rewrites comments and docstrings in one file and renames a local variable to avoid shadowing another name. No executable code behavior changes, so there is no security impact.

Security candidateDrop "scope" from the two ownership-claim summary linesby kdmukai · 8574c13f · Aug 30, 2026 · 1 fileMessage 73 · AdequateInformational 15Details
Commit message · kdmukai

Drop "scope" from the two ownership-claim summary lines

"An output scope claims..." reads no better than "An output claims...".
The word is doing no work in a sentence about what the output says, and
the psbt-scope sense it borrows is not the one the reader needs there.

73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit only rewords two documentation comments inside the code, removing the word 'scope' from error-class descriptions. No program logic, behavior, or security checks were changed.

Security candidateClarify psbt_parser.py docstrings and commentsby kdmukai · 0d374bc7 · Aug 29, 2026 · 1 fileMessage 68 · AdequateInformational 15Details
Commit message · kdmukai

Clarify psbt_parser.py docstrings and comments

No behavior change. Corrects prose that had drifted from what the code
does, and addresses two local names for the same reason.

Docstrings and comments:
- The two ownership-claim exceptions state the condition each signals,
without speculating about how it arises.
- _get_seed_derivation_path drops the output-versus-input severity
split, which _verify_claimed_derivation_paths already states.
- The verified_*_derivation_paths comment says what an entry holds, and
no longer explains the None the positive statement already implies.
- parse() steps 1 and 2 name the per-key condition instead of "scopes".
- _parse_inputs gains a docstring covering the amount total and the
one-shared-policy requirement.
- The single-sig branch comment names the script types it covers.

Naming:
- _parse_multisig's `sc` parameter becomes `multisig_script`.
- A TODO marks _get_policy's local `script`, which shadows the embit
module imported at the top of the file.

68/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit only rewrites comments and docstrings in one file and renames a single function parameter for clarity. The commit message explicitly says 'No behavior change,' and the diff contains no changes to executable logic, control flow, or data handling. There is no security issue here.

Security candidateMerge pull request #1013 from kdmukai/psbt_parser_ownership_scanby Nick Klockenga · e4af6a7e · Aug 29, 2026 · 6 filesMessage 78 · AdequateHigh 78Details
Commit message · Nick Klockenga

Merge pull request #1013 from kdmukai/psbt_parser_ownership_scan

[security] Reject PSBTs that falsely claim this seed's ownership

78/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body✓ Links an issue, advisory, or supporting reference✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol pathmerge-commit duplicate discount
AI analysis · High 78/100

This commit hardens SeedSigner's PSBT (Bitcoin transaction) parser so it no longer trusts a transaction's claim that an output belongs to the user's wallet. Previously, a malicious or buggy transaction could mark a payment to an attacker's address as if it were change going back to the user, potentially tricking the user into approving a theft. The fix re-derives every claimed key from the actual seed and rejects the transaction if the claim does not hold up. It also rejects false claims on inputs and clearly tells the user when the wrong seed was selected.

Security candidateMerge pull request #1005 from kdmukai/psbt_parser_trust_vocabularyby Nick Klockenga · d70b322f · Aug 23, 2026 · 3 filesMessage 73 · AdequateInformational 15Details
Commit message · Nick Klockenga

Merge pull request #1005 from kdmukai/psbt_parser_trust_vocabulary

[refactor] PSBT parsing: rename `change_data`'s unverified fields to `claimed_*`

73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body✓ Links an issue, advisory, or supporting reference
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol pathmerge-commit duplicate discount
AI analysis · Informational 15/100

This commit is a code cleanup that renames internal data fields from 'fingerprint' and 'derivation_path' to 'claimed_fingerprints' and 'claimed_derivation_paths'. It does not change how the software verifies Bitcoin transaction data or how it protects users. The rename is meant to make it clearer to programmers that these values come from an external, untrusted source and should not be trusted until independently verified. The actual security logic is unchanged.

Security candidateImprove comment accuracyby kdmukai · c7eacfbd · Aug 22, 2026 · 1 fileMessage 28 · OpaqueInformational 15Details
Commit message · kdmukai

Improve comment accuracy

28/100 · OpaqueMessage clarity
✓ Subject identifies a change! No meaningful explanatory body! Opaque security-relevant change
Why it was queued
seed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit only rewrites comments and docstrings in a single Python file to make them more accurate. No code logic, behavior, or security properties were changed.

Security candidatePluralize change_data's claimed keysby kdmukai · 4f00d32f · Aug 22, 2026 · 3 filesMessage 68 · AdequateInformational 15Details
Commit message · kdmukai

Pluralize change_data's claimed keys

claimed_fingerprints and claimed_derivation_paths each hold a list
(one entry per cosigner), so the singular key names misread at their
call sites. Also drop a commented-out debugging print.

68/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
seed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit simply renames two internal data keys from singular to plural (e.g., 'claimed_fingerprint' becomes 'claimed_fingerprints') because they actually hold lists of values. It also removes a commented-out debug print line. There is no change to program logic, no bug fix, and no security relevance.

Security candidateImprove comment accuracyby kdmukai · 622a9fed · Aug 22, 2026 · 1 fileMessage 28 · OpaqueInformational 15Details
Commit message · kdmukai

Improve comment accuracy

28/100 · OpaqueMessage clarity
✓ Subject identifies a change! No meaningful explanatory body! Opaque security-relevant change
Why it was queued
seed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit only changes a comment in the source code to make it more accurate. It does not modify any actual code behavior, so it has no security impact on its own.

Security candidateMerge pull request #1002 from kdmukai/psbt_parser_derivation_cacheby Nick Klockenga · 861d6e3c · Aug 20, 2026 · 2 filesMessage 73 · AdequateInformational 19Details
Commit message · Nick Klockenga

Merge pull request #1002 from kdmukai/psbt_parser_derivation_cache

[performance] PSBT parsing: remove redundant BIP32 derivations and Transaction rebuilds

73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body✓ Links an issue, advisory, or supporting reference
Why it was queued
secret or key materialsigning boundaryseed or entropy pathsigning or wallet pathparser or protocol pathmerge-commit duplicate discount
AI analysis · Informational 19/100

This commit is a performance improvement for parsing Bitcoin transaction files (PSBTs). It adds a cache so the wallet doesn't repeatedly recalculate the same cryptographic key derivations, and it avoids rebuilding the whole transaction object many times. The change also removes a small block of code that handled an older multisig script type (p2sh) when deciding whether an output is change. The commit message and code comments describe this only as a performance optimization, not a security fix.

Security candidateMark coordinator-supplied change data as claimedby kdmukai · fc21710b · Aug 15, 2026 · 3 filesMessage 68 · AdequateInformational 15Details
Commit message · kdmukai

Mark coordinator-supplied change data as claimed

Nothing in a psbt proves the fingerprints and derivation paths it
carries, but change_data's fingerprint and derivation_path keys read
like facts -- the change details view splits one into a wallet path and
hands it straight to get_xpub. Renaming them to claimed_fingerprint and
claimed_derivation_path, along with the locals that carry them, puts the
trust boundary in the data itself rather than in the logic around it.

The class docstring now states the convention behind the prefix:
claimed_ for coordinator-supplied metadata, verified_ for what this
device proved by re-deriving from the signing seed, and the invariant
that no verified_ value comes from a claimed_ one without a derivation
in between. It also records that change_data covers self-transfers, not
just change-branch outputs. No logic changes; the two dict keys are the
only behavioral difference.

68/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit is a code cleanup and documentation change, not a security fix. It renames variables and dictionary keys from 'fingerprint' and 'derivation_path' to 'claimed_fingerprint' and 'claimed_derivation_path' to make it clearer that data coming from a PSBT is coordinator-supplied and unverified. No program behavior changes; the same values flow the same way.

Security candidateCorrect the cap comment's memory figureby kdmukai · ab44e970 · Aug 14, 2026 · 1 fileMessage 68 · AdequateInformational 15Details
Commit message · kdmukai

Correct the cap comment's memory figure

The cache costs roughly 650KB at the cap, not 600KB. Drop the claim
that the cap guards against malicious memory exhaustion: the device is
airgapped and stateless, so a psbt that bloats the cache costs the user
a power cycle, not funds.

68/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Informational 15/100

This commit only updates a code comment. It corrects an estimated memory figure from 600 KB to 650 KB and removes an outdated claim that the cache limit protects against malicious memory exhaustion. The actual code behavior and the cache limit value (1000 derivations) do not change. There is no functional security fix or vulnerability being patched.

Security candidateRequire the cache argument on the internal helpersby kdmukai · 5791d55e · Aug 14, 2026 · 2 filesMessage 83 · StrongLow 28Details
Commit message · kdmukai

Require the cache argument on the internal helpers

Defaulting it to None means a call site that forgets it silently
derives without a cache. No caller omitted it; now none can.
_derive_with_cache keeps its optional cache, since that branch is the
plain embit derive() the equivalence test compares against.

83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
seed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Low 28/100

This commit tightens up internal helper functions in a Bitcoin wallet's PSBT parser so that callers must explicitly pass a cache object (or None). Previously, the cache argument defaulted to None, meaning a future coding mistake could silently disable the derivation cache. The change is defensive: it makes accidental omissions impossible without breaking tests. There is no evidence of an exploitable vulnerability in the shipped code, because the commit message itself says no caller actually omitted the argument.

Security candidateScope the derivation cache to parse()by kdmukai · 0a735d58 · Aug 14, 2026 · 2 filesMessage 78 · AdequateLow 36Details
Commit message · kdmukai

Scope the derivation cache to parse()

Nothing outside parse() reads the cache, so it need not be an instance
attribute. As a local, "the cache does not outlive the parse" is a
property of scope rather than a teardown to maintain -- which also
retires the finally block, both initializations, and the test guarding
them.

78/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
seed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Low 36/100

This commit is a code-quality and defense-in-depth change, not a fix for an active bug. It moves a temporary cache of derived Bitcoin keys from being a long-lived object property to a short-lived local variable that exists only during parsing. The cache already had a size cap, so unbounded memory growth was already prevented. The change makes it structurally impossible for the cache to leak beyond a single parse, which slightly reduces the risk that sensitive derived key material could remain in memory longer than necessary. It also removes the explicit cleanup code and one related test because the local variable naturally disappears when parsing finishes.

Security candidateExercise the cache's read path in the equivalence testby kdmukai · 19372889 · Aug 14, 2026 · 1 fileMessage 83 · StrongInformational 12Details
Commit message · kdmukai

Exercise the cache's read path in the equivalence test

Both psbts carried a single change output, so every derivation was a
first-time miss and the cached run only ever wrote to the cache. A
wrong value returned on a hit would have gone unnoticed. Carrying the
change output twice makes the second traversal read back what the
first one cached.

83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundarysigning or wallet pathparser or protocol path
AI analysis · Informational 12/100

This commit only changes a test file. It strengthens an existing test so that it actually exercises the read path of a caching mechanism, rather than only the write path. There is no change to production code, no fix for a vulnerability, and no indication of a security issue being patched.

Security candidateTrim the cache tests down to what catches a regressionby kdmukai · 2b1ce93c · Aug 14, 2026 · 1 fileMessage 83 · StrongInformational 13Details
Commit message · kdmukai

Trim the cache tests down to what catches a regression

"Explain how MAX_CACHED_DERIVATIONS was chosen and test the cap" left
test_cache_does_not_change_parse_output hand-rolling its own comparison
as a tuple of field reprs, and promised to move it onto the new
assert_same_parse_result helper. Do that, and put it on patch.object
like the rest of the suite instead of the monkeypatch fixture.

Then measure what the tests around it are worth, by breaking the cache
eleven different ways and recording which tests notice. Two results
stood out.

The equivalence test is differential: it parses once with the cache and
once without and requires the two to agree, so it can only catch a
break that makes those two runs disagree. Anything in logic they share
passes it. Breaking legacy p2sh change detection and breaking taproot
change detection both went unnoticed here; TestPSBTParser is what
catches those. Running it across four wallet types and both fingerprint
states was therefore buying nothing, and all eight cases detect exactly
what two of them do. Narrow it to one single-sig and one multisig psbt,
the two routes into the cache, and drop the fingerprint-zeroing
variants: test_zero_fingerprint_fill_over_many_inputs is what actually
catches a break in the fill path.

test_derive_with_cache_matches_plain_derive caught nothing that
test_get_cosigners_identical_with_and_without_cache does not also
catch, so remove it.

Nothing covered the parse dropping its cache when it ends. Deleting
that clear leaves the whole suite green, and the cache holds keys
derived from the signing seed, so add a test that asserts the parse
filled the cache it was handed and that the parser is no longer holding
it afterward.

83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundarysigning or wallet pathparser or protocol path
AI analysis · Informational 13/100

This commit only changes test code in tests/test_psbt_parser.py. It trims redundant test cases, switches from monkeypatch to patch.object, adds a helper assertion, and adds a new test verifying that a sensitive in-memory cache is cleared after parsing. There is no change to the actual PSBT parsing or caching logic in the application, so it does not introduce or fix a runtime security vulnerability on its own.

Security candidateHold the parent key in each cache entryby kdmukai · 003deda2 · Aug 14, 2026 · 1 fileMessage 80 · StrongModerate 59Details
Commit message · kdmukai

Hold the parent key in each cache entry

The cache keys on id(parent_key), which is the parent's address in
memory. Python is free to hand that address to a new object once the
original is released, so an entry outliving its parent could be matched
by an unrelated key that landed on the same address, and
_derive_with_cache would return that entry's child instead of deriving
the right one. It fails silently: a recycled address is
indistinguishable from a hit.

Nothing triggers this today, because every parent -- the signing seed's
root and the cosigner xpubs from psbt.xpubs -- is held for the whole
parse. But that is a property of the call sites rather than of the
function, and the failure would be a wrong key in the middle of change
detection.

Storing the parent alongside its derived child keeps that address
occupied for as long as the entry depending on it is alive. The entry
holds a reference to a key the parser already owns, so nothing new is
retained and nothing is copied. Costs 56 bytes per entry, so the cap now
holds the cache to about 600 KB; the comment is updated to match.

Reported by newtonick in review.

80/100 · StrongMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode
Why it was queued
signing boundaryseed or entropy pathsigning or wallet pathparser or protocol path
AI analysis · Moderate 59/100

This commit fixes a subtle caching bug in how SeedSigner derives child keys from parent keys when parsing Bitcoin transactions. The cache used the memory address of the parent key as its identifier, but Python can reuse that address after the parent is discarded. In theory, an unrelated new parent could land on the same address, causing the cache to return the wrong child key without any error. The fix stores the parent key inside each cache entry so its memory address stays occupied as long as the cached result exists. The commit itself says nothing currently triggers the bug because callers keep parents alive, but the function was unsafe on its own.