# trezor-firmware: one monorepo for a hardware wallet, its client and its tests

> SatoshiLabs' firmware monorepo, holding the Rust-based Trezor Core for the T, Safe 3, Safe 5 and Safe 7, the legacy Trezor One firmware in C, a standalone crypto library and NORCOW storage layer shared between them, the Python client library and `trezorctl`, and the protocol definitions that bind them. The release process is not in GitHub, and the licence is not a standard one.

**trezor/trezor-firmware** — :lock: Trezor Firmware Monorepo

- Repository: https://github.com/trezor/trezor-firmware
- Website: https://trezor.io
- Stars: 1,853 · Forks: 817
- Language: C
- License: NOASSERTION
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/trezor-trezor-firmware

## One tree for a device, a client and a protocol

Trezor Firmware is a monorepo, and the directory listing in the README is the clearest description of the project's architecture.

The `core/` directory is Trezor Core, a firmware implementation for the Trezor T, the Trezor Safe 3, the Trezor Safe 5 and the Trezor Safe 7. The `legacy/` directory is the Trezor One firmware implementation, which is the older C codebase. Two directories are explicitly shared between those two: `crypto/`, a stand-alone cryptography library used by both Trezor Core and the Trezor One firmware, and `storage/`, the NORCOW storage implementation, also used by both.

That sharing is the design point of the monorepo. A change to the crypto library or to the storage layout has to be consistent across two very different firmware codebases, and keeping them in one tree with one pull request is how that consistency is enforced.

The `common/` directory holds the things that define the contract between the device and the host: `common/defs/` with the JSON coin definitions and support tables, `common/protob/` with the common protobuf definitions for the Trezor protocol, and `common/tools/` for managing the coin definitions and related data.

The `python/` directory is the Python client library, published on PyPI as `trezor`, and the `trezorctl` command. The `tests/` directory is the firmware unit test suite, `tools/` is miscellaneous build and helper scripts, `ci/` is the ancillary files, data and scripts for the CI pipeline, `docs/` is assorted documentation, and `vendor/` holds submodules for external dependencies.

The licence is a custom one that GitHub cannot classify, with a `LICENSE.md` and a `COPYING` file at the root, and the homepage is trezor.io. The last push was on 2026-10-18.

## The Makefile is a checklist of what must not break

The Makefile is long, and reading its target names is the most efficient way to understand what this project considers its invariants.

There is a `style_check` target that runs the individual checks together, and the list covers a lot more than formatting. There is `pystyle_check`, `cstyle_check`, `protostyle_check` and `ruststyle_check` for the four languages in the tree, `changelog_check` for the changelog format, `translations_style_check` for the translation JSON, `yaml_check` and `editor_check` for configuration and editor definitions, and `workflow_timeout_check` for the CI workflows.

Then there are the ones that reveal the production process. `bootloader_hashes_check` and `hsm_keys_check` are about the artefacts a device trusts at boot and the keys it holds. `certs_check` is about certificates. `prodtest_error_codes_check` is about the error codes in the production test suite. `tropic_config_check` is about the configuration of a Trezor security chip family, and `lsgen_check` is about a generated source file, with a matching `gen_check` and a `mocks_check` for the mocks.

The `typecheck` target runs pyright, and there is a `pyrightconfig.json` at the root, so the Python side is statically type checked, with `flake8-annotations` in the dependency list to make sure the annotations are real.

The `protostyle` and `protobuf` targets are for the protocol definitions, and `templates_check` and `solana_templates_check` are for the device UI templates, one set for the core firmware and one set for Solana support, which is the clearest sign in the tree of how many assets a wallet firmware actually carries.

The `style` target applies all the styles at once, and the file lists how the sources are discovered, with separate find commands for the Python, C, protobuf and Rust sources.

## Pinning dependencies by date, and a Python toolchain with no room to drift

The `pyproject.toml` is worth reading in detail, because it is a policy document as much as a dependency list.

The project requires Python 3.11 or newer and below 4, and the dependencies are pinned hard. `protobuf==6.33.5` with a comment explaining that the minor version needs to be higher than the version in `shell.nix`, `nanopb>=0.4.3,<0.5`, `pytest>=9.0.3,<10`, `coverage==7.13.5`, `shamir-mnemonic`, `slip10`, `fido2`, `python-bitcoinlib`, `mnemonic`, `stellar-sdk`, `embit` and `construct-typing` among them.

The ones that are not obvious from a list of names are the interesting ones. `shamir-mnemonic` and `slip10` are the seed handling and the hierarchical key derivation, which for a hardware wallet are the pieces where a mistake is unrecoverable. `stellar-sdk` and `embit` are for Stellar and for Bitcoin script handling. `fido2` is for the FIDO2 second factor. `hypothesis` is in the list, which means the test suite does property-based testing rather than only example-based testing, and `intelhex` and `imgtool` are Intel firmware tooling, and `toiftool` is for the firmware image format, with `pyelftools` and `binsize` for inspecting the produced binaries.

Then there is the uv configuration, and it is the part that explains the whole approach:

```toml
[tool.uv]
package = false
exclude-newer = "30 days"
```

The resolver is told to exclude any package published in the last thirty days, and then three specific packages are given explicit dates, urllib3 at 2026-09-15, mako at 2026-09-22 and virtualenv at 2026-10-02. That is a deliberate policy of building only from packages that have been out for a month, which is how a firmware project avoids discovering that one of its dependencies shipped a breaking change the night before a release.

The dev group adds `scan-build`, the clang static analyser, `towncrier` for the changelog and `wheel`, and there is a `uv.lock` committed at the root.

## Contributing, and the security process

The contribute section is short and it points at three things.

The first is the contributing guide, `docs/misc/contributing.md`. The second is the commit convention, and the wording is notable: using Conventional Commits is strongly recommended and might be enforced in the future. The third is the documentation, either the `docs` folder or docs.trezor.io, with the misc chapter called out in particular as containing useful assorted knowledge.

That last recommendation is a good sign. A documentation repository that tells contributors to read its miscellaneous chapter first is a project where the knowledge is in the repository rather than in the heads of a few people.

The security vulnerability disclosure section is unambiguous. Please do not create publicly viewable issues for suspected security vulnerabilities, and the instructions are on the Security tab of the repository, which is the private reporting process.

That is the right policy for a hardware wallet project, and it is worth contrasting with the general advice in the same repository, which invites issues and pull requests for everything else.

The repository root also carries `CODEOWNERS`, which assigns review responsibility by path, and `AGENTS.md` and `COMMITS.md`, and a `CONTRIBUTING.md` at the top level as well as the one in the docs. The `create_monorepo.py` script at the root tells you the tree was assembled by a tool, and `build-docker.sh` and `shell.nix` are the two supported ways to get a build environment, with the Nix shell pinning the toolchain that the Python comment above refers to.

## The tooling around the build, and what it implies

A handful of root files say more about how this project is built than the README does.

`shell.nix` is a Nix shell, which is how the whole toolchain, the compilers for the C and Rust parts, the protobuf compiler and the Python environment are pinned to one another. In a monorepo that compiles firmware for several devices, a C library and a Rust codebase, and hosts a Python client, an unpinned toolchain is the first thing to break.

`build-docker.sh` is the containerised alternative, and `.clusterfuzzlite/` at the root is the integration point for ClusterFuzzLite, which is the continuous fuzzing infrastructure used on projects that take untrusted input seriously. A hardware wallet that parses data from a host device, from a companion application and from the blockchain is a parser-heavy attack surface, and fuzzing it is not optional.

`pyrightconfig.json` and `.pylintrc` cover the Python, `.yamllint.yml` covers the workflow and configuration YAML, and `.clang-format` and `.editorconfig` cover the C and the repository generally. `tbench.toml` is a benchmark configuration, and `.gitmodules` with the `vendor/` directory is how external dependencies are pulled in.

The `nordic/` directory is a further platform in the tree, alongside `core/` and `legacy/`, which is how a monorepo for a hardware wallet accommodates a third target without pretending the two older ones are going away.

Taken together, the root tells you this is a project where the build system is the product. A firmware release is an artefact with hashes, a bootloader, keys, certificates and a device tree of templates, and every one of those has a check target in the Makefile.

## Where trezor-firmware is the wrong thing to depend on

The first wrong case is a team that wants a wallet library. This is firmware and a client, not a library, and the delivery mechanism is a signed image installed on a device. A service that needs to hold keys or sign transactions programmatically belongs with the protocol and the client, not with the firmware tree.

The second is a team that needs a permissive licence. The licence is a custom one that GitHub cannot classify, with `LICENSE.md` and `COPYING` at the root, and a hardware wallet vendor's terms are not the MIT or Apache terms a product team would assume. Read the files before assuming anything.

The third is a developer who wants the newest code. There are no GitHub releases in this repository, so the versions of the firmware, the client and the protocol definitions you get are whatever a branch gives you, and the branches for the different devices and the client move at different speeds. The Python client is the exception, since that one is published on PyPI and can be versioned normally.

The fourth is a contributor who expects a normal open source workflow. Conventional Commits are strongly recommended and might be enforced, the security process is a private one, and the style gate covers a dozen languages and formats. This is a process designed for a security-critical product, and the friction is a consequence of that rather than an accident.

The fifth is a team that wants to fork the whole thing. The monorepo is large, the vendored submodules and the pinned toolchain are part of the design, and a fork that does not keep up with the upstream protocol definitions will not interoperate with the current client.

What the repository is for is the work itself. If you are writing a Trezor integration, the `common/protob/` definitions and the Python client are the parts you actually need, and the firmware is the reference implementation that tells you what the device will do.

## Conclusion

trezor-firmware suits a developer working on Trezor hardware, on the protocol definitions that the client, the firmware and the device share, or on the wallet's cryptography and storage layers, and who wants one tree rather than five repositories. It does not suit a team that wants to run a wallet as a service, since the delivery mechanism is signed firmware images rather than a library, and it does not suit a team that needs a permissive licence, because the licensing is a custom one. Before contributing, read `docs/misc/contributing.md` and the `misc` chapter of the documentation, and note that the project asks you to use Conventional Commits, that security issues must go through the private reporting process rather than the public issue tracker, and that the build pins dependency resolution by date, so a reproducible build is a deliberate feature.

## FAQ

### What is in the trezor-firmware monorepo?

Trezor Core for the T, Safe 3, Safe 5 and Safe 7 in `core/`, the legacy Trezor One firmware in `legacy/`, a stand-alone crypto library and a NORCOW storage implementation shared by both, the JSON coin definitions and protobuf protocol definitions under `common/`, the Python client library and `trezorctl` in `python/`, the firmware unit tests, the CI files, the docs and the vendored submodules.

### How does the trezor-firmware build pin its dependencies?

The `pyproject.toml` sets `exclude-newer = "30 days"` for the uv resolver, so only packages published more than a month ago are considered, and three packages, urllib3, mako and virtualenv, are given explicit allowed dates. The `shell.nix` pins the rest of the toolchain.

### How should I report a security issue in trezor-firmware?

Not as a public issue. The README asks that publicly viewable issues are not created for suspected security vulnerabilities and points to the repository's Security tab for the private reporting instructions.

### What checks does the trezor-firmware Makefile run?

Style checks for Python, C, Rust and protobuf, plus changelog, translation, YAML, editor and workflow timeout checks, and production checks including bootloader hashes, HSM keys, certificates, production test error codes, tropic configuration, generated sources, mocks and templates.

### What does the trezor-firmware project use the Python client for?

The `python/` directory holds the Python client library, published on PyPI as `trezor`, and the `trezorctl` command, alongside the protobuf definitions in `common/protob/` that define the protocol the device and the client share.

## Sources

- [Issues](https://github.com/trezor/trezor-firmware/issues)
- [Project website](https://trezor.io)
- [README](https://github.com/trezor/trezor-firmware/blob/main/README.md)
- [trezor/trezor-firmware on GitHub](https://github.com/trezor/trezor-firmware)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/trezor-trezor-firmware
