more eyes

What Can You Check About an embit Release?

The release note’s questions, put to one library

This note applies What Can You Check About a Software Release? to one library. It records what the public artifacts show. It does not assess the library’s code.

Document embit-release-records.md
Version 0.6 — peer review draft
Audience People who install embit or depend on a project that does: read Start here. Technical reviewers: read the full note.
Method The method of the release note, version 0.3. Separate three checks — public, signed, rebuilt — and tie each claim to a public artifact. Record a figure with the listing it came from and the date that listing was read.
Changes See the changelog.
Scope The embit library as published: the file on the Python Package Index (PyPI), the release tags in the diybitcoinhardware/embit repository, and the project’s written release process. This is not a review of the library’s cryptography, a finding about any person, or a review of any wallet or device that uses the library. Signing-device firmware is out of scope.

Contents

Start here

This section is for anyone who installs embit, or who uses a wallet that includes it. You do not need to read code to use it. The rest of the note is the record behind each point.

What embit is. embit is a Bitcoin library written in Python. Other projects build on it, including software for signing devices. A person can install embit from PyPI, the public index of Python packages. A project can also copy embit straight from its source repository.

Three things the public record shows. Each one was read on 9 October 2026.

  1. The file on PyPI is version 0.8.0, published in May 2024. PyPI has no newer version. See §3.1.
  2. Versions 0.8.1 and 0.8.2 exist as signed tags in the source repository. A tag is a named point in the source history. Neither version is a file on PyPI. See §3.2.
  3. The project wrote a new release process in version 0.8.1. The process builds and publishes from an automated system, with checksums and build records. No run of that process has completed publishing yet. See §3.3 and §3.4.

The library is between two ways of publishing. The file on PyPI comes from the earlier way. The written rules describe the later way. Most of this note follows from that one fact.

What you can check today. The answer depends on how you get the library.

How to judge an answer. A good answer shows you something you can open. A weak answer repeats the claim in different words. A weak answer does not prove a problem. It shows that the claim is not proved yet.

One thing a release record cannot show. embit holds two versions of its core signing code. One calls a widely used native library. The other is written in Python and takes over when the native library does not load. The release file holds both. It does not tell you which one runs on your machine. See §3.5.

What this note does not do. It does not say whether embit is safe to use, and it does not compare embit with other libraries.

1. What the note establishes

A release claim needs a defined object. For embit on 9 October 2026 there were three: a file on PyPI, two later tags in the source repository, and a written process that describes future files. They are different objects, and a statement about one does not carry to another.

This note uses public pages, public repository contents, and the public record of one automated run. No account setting, private configuration, or private conversation was read. The note reports what those sources show on the date read. The project is changing its release process, so a later reading can differ.

1.1 Evidence

Each record in §3 has: claim; object and date; public source; what was read; result; unresolved. The fixed entries “Not applicable” and “Not read for this note” have the meanings in the release note §1.1.

1.2 Terms

Terms from the release note §1.2 keep their meaning. This note adds the following.

Term Meaning here
Package index A public service that stores release files for a programming language. PyPI is the package index for Python.
Source distribution (sdist) A release file that holds a project’s source and packaging data.
Wheel A release file in a ready-to-install format.
Lightweight tag A name for a commit. It has no signature of its own. The commit it names can still carry a signature.
Annotated tag A tag stored as its own object, with a tagger and a date. It can carry a signature.
Trusted Publisher A PyPI feature. An automated build system proves its identity to PyPI and receives a short-lived upload token, in place of a stored password or token.
PyPI attestation A signed statement that binds a release file to a digest of its contents. PyPI accepts attestations from Trusted Publisher identities.
Vendoring Copying a library’s source into another project, in place of installing it from a package index.

PyPI Trusted Publishers, PyPI attestations

2. A library between two release processes

Date Event Source
30 May 2024 Version 0.8.0 is tagged and uploaded to PyPI as one source distribution. The archive includes seven prebuilt libsecp256k1 libraries. §3.1
28 April 2026 A commit titled “secp256k1: stop shipping prebuilt native binaries” removes those libraries from the source tree. §3.2
2 June 2026 Version 0.8.1 is tagged with a signed annotated tag. The same version adds a release document, a release workflow, a security policy, and a package content policy. §3.2, §3.3
8 August 2026 Version 0.8.2 is tagged with a signed annotated tag. The release workflow starts and is cancelled. §3.2, §3.4
9 October 2026 PyPI lists 0.8.0 as the latest version. §3.1

Two groups of people get different objects.

The project’s security policy says: “Security fixes are provided for the latest PyPI release line.” On the date read, the changes in 0.8.1 and 0.8.2 were in the repository and not on PyPI.

3. Public records

3.1 The 0.8.0 file on PyPI

Claim. PyPI holds one file for embit 0.8.0, a source distribution. The file has a published hash. PyPI shows no attestation for it. The archive holds Python source and seven prebuilt libraries.

Object and date. embit-0.8.0.tar.gz, read 9 October 2026 through the PyPI project page, JSON API, simple index, and integrity API. The archive was downloaded and hashed the same day. Its regular files were compared with the generated source archive for commit 84cce66fb831fa6d625fb73f28e03605f3c04e28, the target of tag v0.8.0.

Public source. pypi.org/project/embit/. pypi.org/pypi/embit/json. pypi.org/simple/embit/. The PyPI integrity API for the file. The diybitcoinhardware/embit repository.

What was read. The version list. The file record for 0.8.0. The integrity response. The archive member names. The SHA-256 of the downloaded file. The tag and commit for v0.8.0. The regular-file contents of the PyPI archive and the generated source archive for that commit, compared after removing each archive’s top-level directory name.

Result.

A hash on an index page is a fingerprint. A matching hash shows that a downloader received the file the index holds. It does not identify who built the file.

Unresolved. The seven additional packaging files were not regenerated from the tagged source. The prebuilt libraries were not rebuilt, and they were not tied to a public libsecp256k1 commit. The signature on commit 84cce66 was not verified outside the hosting service. No second builder’s report was found for this file.

3.2 The 0.8.1 and 0.8.2 tags

Claim. Tags v0.8.1 and v0.8.2 are annotated and signed. Each names a commit in the public history. Neither tag has a release file on PyPI.

Object and date. The two tag objects and the commits they name, read at repository commit 2b375a3 on 9 October 2026. The release page for v0.8.2, read the same day.

Public source. The diybitcoinhardware/embit repository. The hosting service’s tag verification record and release page.

What was read. The tag objects. The signature header of each named commit. The source tree at each tag. The submodule pin. The verification record. The release page.

Result.

A tag signature vouches for the tag and, through it, for a commit. It does not vouch for a file built from that commit. No project-built source distribution or wheel is published for these two tags in the records read. The generated source archives are separate release files; this note did not compare their contents with the signed tags.

Unresolved. No public key was retrieved, and no signature was verified locally. The note did not establish how a reader would tie the key to the project through a second channel. The automatically generated source archives were not compared with the tag.

3.3 The written release process

Claim. From version 0.8.1 the repository holds a written release process. The process calls for publishing only from an automated workflow, through a PyPI Trusted Publisher. The workflow generates checksums, a software bill of materials, and build attestations; their destinations differ. The process requires release files without bundled native libraries.

Object and date. RELEASING.md, SECURITY.md, docs/package-content-policy.md, and .github/workflows/release.yml, at repository commit 2b375a3, read 9 October 2026.

Public source. The diybitcoinhardware/embit repository and the pinned publishing action in pypa/gh-action-pypi-publish.

What was read. The release document. The security policy. The package content policy. The release workflow file. The attestations input in pypa/gh-action-pypi-publish/action.yml at the workflow’s pinned commit cef221092ed1bacb1cc03d23a2d87d1d172e277b, read 9 October 2026.

Result.

A written process is evidence of what the project intends. The record of a completed run would be evidence of what was done. §3.4 covers the one run so far.

Unresolved. Three settings that the process relies on are not visible in the repository files: the protection rules on the default branch, the required reviewers on the pypi environment, and the Trusted Publisher configuration on PyPI. The note did not establish how release-page files would be published to satisfy the post-publish checklist.

3.4 The release run for 0.8.2

Claim. The release workflow has run once, for tag v0.8.2. A maintainer account cancelled the run before the build job finished. The run published no file.

Object and date. Release workflow run 31264239479, attempt 1, for commit eb6104fd85d3becabba628756cd5e1b75619f3a1, read 9 October 2026 through its public run page and job records.

Public source. The workflow run list and the run page in the diybitcoinhardware/embit repository.

What was read. The run’s trigger, conclusion, annotations, job results, and artifact list.

Result.

A cancelled run is a different fact from a failed check. The record shows that someone with access stopped the run. It does not show why.

Unresolved. The reason for the cancellation was not read. The note did not read whether a later release is planned through this workflow.

3.5 Two implementations in one release

Claim. An embit release holds two implementations of its secp256k1 operations: bindings to a native libsecp256k1, and a pure-Python implementation. The library chooses between them when it is imported. The release record does not determine which one runs.

Object and date. The source trees at tags v0.8.0 and v0.8.2, the project README at both tags, and the upstream counterpart in the Bitcoin Core repository at commit 4bacf21, read 9 October 2026. Pull request 135 in the embit repository, read the same day.

Public source. src/embit/util/secp256k1.py, src/embit/util/py_secp256k1.py, src/embit/util/key.py, src/embit/util/ctypes_secp256k1.py, src/embit/misc.py, and README.md in the diybitcoinhardware/embit repository. test/functional/test_framework/key.py in the Bitcoin Core repository.

What was read. The selection code. The header and the signing, nonce, and key-generation functions of the pure-Python implementation. The README text on backends. The header of the upstream counterpart at the inspected commit. The native library search paths, context initialization, and context-randomization wrapper. Calls to generate_privkey() and ECKey.generate() in the library source at v0.8.2. The title, description, and review comments of pull request 135.

Result.

Two implementations in one file is a release fact: a reader who has verified the file has not yet learned which code runs. The custody note calls this the fourth level of build evidence, “evidence that the device runs that binary.” See Who Can Move Your Bitcoin? §3.

This record quotes what the files say about themselves. It does not assess whether the pure-Python implementation is fit for a given use.

Unresolved. The note did not test which implementation loads on any platform. It did not measure the pure-Python implementation for timing behavior, and it did not compare the embit copy with the upstream counterpart line by line or establish the historical revision copied. It did not read which implementation any downstream project runs, beyond the check in §6. The note did not read the MicroPython secp256k1 module.

4. The three checks, by object

Check The 0.8.0 file on PyPI Tags v0.8.1 and v0.8.2
Public The source history is public, and tag v0.8.0 names a commit in it. The 60 shared regular files match the tagged tree, including seven prebuilt libraries; seven additional files are packaging metadata. The native libraries remain public as finished files without an established link to their source and build inputs. The source history is public. Each tag names a commit that a reader can open.
Signed No signature on the file; PyPI did not accept PGP signatures at the upload date. No PyPI attestation. The commit behind the tag carries a signature. Each annotated tag carries a signature that the hosting service reports as valid. The fingerprint was recorded. Public-key retrieval, authentication, and local signature verification were not performed.
Rebuilt No second builder’s report was found. Not applicable to the tag objects. No project-built source distribution or wheel was found. Generated source archives are separate files whose correspondence with the tags was not checked.
What the downloader checks The file’s hash against the hash that PyPI lists. That step uses no key. The tag signature, after the reader has a reason to tie the key to the project.

The public check shows that a change can be read. It does not show that anyone read it.

5. Beside the four processes

The rows below are the rows of the release note §4.1. To compare, read each row here beside the same row there. A library that other projects copy is a different kind of object from a program that people download and run. Several rows are “Not applicable” for that reason.

Step embit, as read 9 October 2026
What the project publishes One source distribution on PyPI (0.8.0). Signed tags for 0.8.1 and 0.8.2, with generated source archives on the 0.8.2 release page. The written process describes a source distribution and a wheel.
Where the release ties to source history Public tags. v0.8.0 is a lightweight tag on a signed commit. v0.8.1 and v0.8.2 are signed annotated tags.
Object that is signed For 0.8.0, the commit. For 0.8.1 and 0.8.2, the tag. No signature on a release file.
Who signs The key named by the commit or tag signature. The note relies on the hosting service’s verification record and does not authenticate the key holder.
Where a reader retrieves keys Not read for this note. Only a fingerprint was recorded; no public key was retrieved.
What the documents offer to authenticate a key No key fingerprint in the repository files read.
Rebuild in the written process Not in the written process. The workflow is the one builder.
What a failed rebuild blocks Not applicable. The written process has an approval step before publishing, and no rebuild step.
How a builder is added Not applicable.
Inputs that builders share Not applicable.
What the downloader checks From PyPI: the file’s hash against the index. From the repository: the tag signature.
Other public records A release document, a security policy, and a package content policy. The workflow names a software bill of materials and build attestations; no completed run has produced them.

In one respect embit’s tags resemble the kernel record in the release note: a developer’s signature names a point in the source history, and whoever compiles or copies that source later makes a new object with its own record.

6. Downstream copies

These entries show which object a downstream project names. They are not reviews of those projects.

A pin to a hash shows which file a project named. A pin to a commit shows which source a project named. Neither pin signs anything, and neither covers the image that the downstream project builds afterwards. That image is a downstream build with its own record.

7. Questions that make a claim reviewable

The claims are from the release note §6.

If the claim is What the record shows Artifact that would complete it
The source is open A public repository. All 60 regular files shared by the 0.8.0 archive and tagged tree match, including seven native libraries; seven additional files are packaging metadata. Evidence linking the bundled native libraries to public source and the build inputs that produced them. Matching finished binaries does not supply that link.
It is signed A signed commit for 0.8.0. Signed tags for 0.8.1 and 0.8.2. A signature or attestation on a release file, and a published way to authenticate the key
Anyone can reproduce it A workflow that builds once A second builder’s signed report of the same hash for a published file
We publish provenance Separate build-provenance steps and PyPI attestations enabled in the workflow. The cancelled run produced neither. The attestation for a published file, and who issued it
Releases follow the written process The process, and one cancelled run. The workflow has no release-page upload step. A completed publishing run and evidence that the release-page files and post-publish checks satisfy the written process
The artifacts are pure Python No .so, .dll, or .dylib files were found in the source trees at v0.8.1 and v0.8.2. The 0.8.0 file on PyPI predates the rule and holds seven prebuilt libraries. A published file built under the rule
It uses libsecp256k1 Bindings to a native library, with a whole-module fallback and conditional per-function Python replacements (§3.5) Evidence identifying the loaded native library and the implementation selected for each relevant operation, or a demonstrated configuration that excludes Python fallback paths. Native-library presence or an import that requires it does not alone rule out per-function fallback.
You can verify it yourself A hash comparison for the PyPI file. A tag signature for the repository. A verification step for a release file that names a key

A missing artifact means only that this note does not show the claim.

8. Limits and source use

9. References

Each source is listed once with the version or date consulted and the sections that cite it.

embit, at diybitcoinhardware/embit commit 2b375a3.

Reference What it defines Cited in
RELEASING.md The written release process §3.3, §7
SECURITY.md Supported versions and release integrity notes §2, §3.3
docs/package-content-policy.md What a published file may contain §3.3
.github/workflows/release.yml The release workflow §3.3, §3.4
CHANGELOG.md Changes in 0.8.1 and 0.8.2 §2, §3.2
util/secp256k1.py at v0.8.2 Selection between implementations §3.5
util/py_secp256k1.py at v0.8.2 The pure-Python module §3.5
util/key.py at v0.8.2 Pure-Python signing, nonce, and key-generation functions, and the stated origin §3.5
util/ctypes_secp256k1.py at v0.8.2 Native library search, initialization, and context-randomization wrapper §3.5
misc.py at v0.8.2 General random helpers §3.5
util/ctypes_secp256k1.py at v0.8.0 Prebuilt-library lookup and loading §3.5
README.md at v0.8.2 The documented backend order and fallback §3.5
README.md at v0.8.0 The documented fallback for the 0.8.0 file §3.5
Pull request 135 The open change that removes the pure-Python module, read 9 October 2026 §3.5
Source tree for v0.8.0 Commit 84cce66 and its source tree §3.1
Source tree for v0.8.1 Commit b5d694a and its source tree §3.2
Source tree for v0.8.2 Commit eb6104f and its source tree §3.2, §6
Signed tag object for v0.8.1 Tag object, target commit, signature, and hosting-service verification §3.2
Signed tag object for v0.8.2 Tag object, target commit, signature, and hosting-service verification §3.2
Generated source archive, commit 84cce66 Tagged-tree regular files used in the archive comparison §3.1
Commit 92e016b Removal of the prebuilt libraries §2, §3.2
Release page for v0.8.2 Assets and tag verification, read 9 October 2026 §3.2
Release workflow runs The one run, read 9 October 2026 §3.4
Release run 31264239479, attempt 1 The cancelled run for commit eb6104f, read 9 October 2026 §3.4
Release run job records Step conclusions for attempt 1, read 9 October 2026 §3.4

Publishing action, at commit cef221092ed1bacb1cc03d23a2d87d1d172e277b, read 9 October 2026.

Reference What it defines Cited in
pypa/gh-action-pypi-publish/action.yml The enabled-by-default PyPI attestation input §3.3

PyPI, read 9 October 2026. The project listings can change; the archive and integrity endpoint below name version 0.8.0.

Reference What it defines Cited in
embit project page Versions, upload date, and the Trusted Publishing answer §3.1
embit JSON record The file record and digest §3.1
embit 0.8.0 source distribution The archive hashed, counted, and compared §3.1
embit 0.8.0 integrity record The response reporting no provenance for this file §3.1
embit simple index The file link and digest §3.1
Removing PGP from PyPI, 23 May 2023 The end of PGP signature uploads §3.1
Trusted Publishers The Trusted Publisher feature §1.2
Attestations PyPI attestations §1.2

Bitcoin Core, at bitcoin/bitcoin commit 4bacf21.

Reference What it defines Cited in
test/functional/test_framework/key.py The upstream counterpart at the inspected commit and its test-only warning §3.5

Python, version 3.10 documentation, read 9 October 2026.

Reference What it defines Cited in
random — Generate pseudo-random numbers The default generator and its unsuitability for cryptographic purposes §3.5

Downstream projects

Reference What it defines Cited in
SeedSigner requirements.txt, commit b2199c6 The pin to embit 0.8.0 §6
seedsigner-os python-embit, commit c4cb767 The hash pin and the library check script §3.1, §6
verify-secp256k1-binary.sh, commit c4cb767 Filename, module-presence, and fallback-absence checks §6
Krux, commit 9b3a431 The submodule pin and the library build task §6

Changelog

Newest first. Versioning follows STYLE.md. This note uses the tag prefix embit-v because the repository’s unprefixed version tags already identify the custody note. Each version from 0.2 links to its tagged text. Version 0.1 was not committed to this repository, so it has no tag. The tags for 0.2 and 0.4 were created on 11 October 2026.

Version Change
0.6 — 9 October 2026 Content commit. Makes explicit that default pure-Python signing paths do not call a CSPRNG and that generate_privkey() uses random.randrange, whose default generator is not a CSPRNG (§3.5). Adds the Python documentation reference and updates the README. Version tag: embit-v0.6.
0.5 — 9 October 2026 Content commit. Corrects the caller of generate_privkey(), conditional replacements for optional native functions, and the distinction between native context initialization and caller-supplied randomization (§3.5). Qualifies library loading, nonce randomness, and the historical origin of the copied code. Requires evidence for each relevant operation when identifying the implementation in use (§7). Adds pinned loader and random-helper references, corrects source-version limits, and updates the README. Version tag: embit-v0.5.
0.4 — 9 October 2026 Adds §3.5: the release holds a native binding and a pure-Python implementation of its secp256k1 operations, chosen at import without a signal. Records the selection code, the documented fallback, the stated origin of the pure-Python code and the warning in the origin file, how nonces are derived, and the open change that removes the pure-Python module. Adds a Start here paragraph, a §7 row, and a §8 limit.
0.3 — 9 October 2026 Content commit. Records the byte-for-byte match of all 60 regular files shared by the 0.8.0 PyPI archive and tagged tree, including the seven native libraries, and identifies seven additional packaging files (§3.1). Keeps native-library build provenance and regeneration of packaging metadata unresolved. Corrects fingerprint retrieval versus public-key retrieval and local verification (§3.2, §4, §5). Records both signed tag-object IDs and distinguishes generated source archives from project-built distributions. Separates workflow output destinations, records the missing release-page upload step, and confirms that the pinned publishing action enables PyPI attestations separately from the build-provenance steps (§3.3). Pins the cancelled run and attempt, names its cancelled smoke-install step (§3.4), and narrows the downstream script’s checks to what it performs (§6). Updates the claims table, source-use limits, references, and README. Version tag: embit-v0.3.
0.2 — 9 October 2026 Restructured to match the other notes: Start here, contents, numbered records, references pinned to commits. Reframed around the change between two release processes (§2). Removes the names of people. Adds that the commit behind tag v0.8.0 is signed; that PyPI did not accept PGP signatures at the upload date; that the prebuilt libraries are in the public source tree at v0.8.0 and were removed by a recorded commit before 0.8.1; that the release page lists two generated source archives; and that a maintainer account cancelled the release run. Closes three unresolved items: the ancestry of the 0.8.2 commit, the submodule commit, and whether 0.8.0 was uploaded through a Trusted Publisher. Adds the security policy and the package content policy as sources. Records that one downstream project pins the 0.8.2 commit and that another checks the library in its built image. Removes the table of counts.
0.1 — 9 October 2026 First draft, read against release note 0.3. Records for the PyPI file and the later tag, the six questions, a table of counts, a comparison column, downstream pointers, and a claims table.