keycard is a command line tool to manage Status Keycards — programmable NFC/USB hardware wallets for Ethereum and other cryptographic operations.
- Dependencies
- Installation
- Building from Source
- Releases
- Cutting a Release
- Global Flags
- Environment Variables
- CLI Commands
- A USB smart card reader (contact readers are more reliable than NFC readers).
- On Linux, install and run the PC/SC daemon.
Download the pre-built binary for your platform from the releases page and rename the file to keycard, removing the platform-specific suffix.
| Asset | Runs on | Runtime requirements |
|---|---|---|
keycard-linux-amd64 |
Linux, x86_64 | pcscd and libpcsclite.so.1; built on Ubuntu 24.04, so it needs glibc >= 2.39 |
keycard-linux-arm64 |
Linux, aarch64 | same as above |
keycard-darwin-arm64 |
Apple Silicon Macs | none (PC/SC ships with macOS) |
keycard-darwin-amd64 |
Intel Macs | none |
keycard-windows-amd64.exe |
Windows 10/11, x86_64 | none (winscard.dll is part of Windows) |
keycard-windows-arm64.exe |
Windows on Arm | none |
Every release carries a SHA256SUMS file covering all assets. Verify after downloading:
sha256sum -c SHA256SUMS # in the directory holding the binariesOn Linux, install the PC/SC runtime if it is missing (Debian/Ubuntu: apt install libpcsclite1 pcscd).
make build
./build/bin/keycard --helpTo run tests:
make testBuilding needs a C toolchain and the PC/SC headers, because the smart card
bindings are cgo: Debian/Ubuntu libpcsclite-dev, Fedora pcsc-lite-devel,
Arch pcsc-lite, macOS needs only the Xcode command line tools (the PCSC
framework is part of the SDK).
To produce a release-named binary plus checksums in dist/:
make distBinaries for all supported platforms are built and published by GitHub Actions:
.github/workflows/ci.ymlruns on pull requests: formatting,go vet, tests, plus a compile of every release target so cross-platform breakage surfaces before it is tagged..github/workflows/build.ymlis a reusable workflow holding the platform matrix; both CI and the release call it, so the matrix exists only once..github/workflows/release.ymlruns onv*tags and publishes the GitHub release with all assets andSHA256SUMS.
The matrix is dictated by cgo, not by preference:
| Platform | Runner | CGO | Why |
|---|---|---|---|
| linux/amd64, linux/arm64 | ubuntu-24.04, ubuntu-24.04-arm |
on | scard binds libpcsclite through cgo, so it must be built natively on each architecture |
| darwin/arm64 | macos-latest |
on | links the PCSC framework from the macOS SDK, so it must run on a Mac |
| darwin/amd64 | macos-latest |
on | cross-built for Intel with clang -arch x86_64 against the universal SDK |
| windows/amd64, windows/arm64 | ubuntu-24.04 |
off | the Windows backend of scard is pure Go (winscard.dll via syscall), so no Windows runner or MinGW toolchain is needed |
-
Bump
VERSION(the value is embedded in the binary and shown bykeycard version). -
Tag and push:
git tag v4.0.1 git push origin v4.0.1
The workflow strips the leading
v, sov4.0.1produceskeycard version 4.0.1. -
Watch it in the Actions tab; the release appears with all six binaries,
SHA256SUMS, and auto-generated release notes.
The same workflow can be started manually (Actions -> Release -> Run workflow) to rebuild the assets of an existing release or to publish a pre-release: pass the tag input, and optionally prerelease / draft. A missing tag is created at the selected commit.
The publishing job needs write access to releases. If your organisation keeps the workflow token read-only, enable Settings -> Actions -> General -> Workflow permissions -> Read and write permissions (the job only asks for
contents: write).
The make build-platforms / make release targets (xgo plus github-release) are the old Jenkins-era path and are kept for reference only; note that make release deletes and recreates the release with the given tag.
The following flags are available on all commands:
| Flag | Description |
|---|---|
--log-level <level> |
Log level: debug, info, warn, error (default: info) |
--pin <pin> |
PIN for card authentication (or KEYCARD_PIN env var) |
--puk <puk> |
PUK for card unblocking (or KEYCARD_PUK env var) |
--pairing-password <pw> |
Pairing password for V1 cards (or KEYCARD_PAIRING_PASSWORD env var) |
--reader <name> |
Specific reader name to use (auto-detect if omitted) |
--json / -j |
Output in JSON format |
--yes / -y |
Skip interactive confirmations |
--card-ca <hex> |
CA public key for V2 certificate verification (hex, 33 bytes compressed) |
--test-card |
Shortcut for --card-ca with the test CA public key |
--whitelist-card <hex> |
Whitelisted card identity public key (hex, 33 bytes compressed) |
--show-secrets |
Show secrets (PIN, PUK, pairing keys) in output. Hidden by default |
Security tip: Passing credentials like
--pin,--puk,--pairing-password,--new, or--new-pinon the command line exposes them in your shell history andpsoutput. Prefer using theKEYCARD_PIN,KEYCARD_PUK,KEYCARD_PAIRING_PASSWORD,KEYCARD_NEW_PIN,KEYCARD_NEW_PUK, andKEYCARD_NEW_PAIRING_PASSWORDenvironment variables instead.
| Variable | Description |
|---|---|
KEYCARD_PIN |
Default PIN for card authentication |
KEYCARD_PUK |
Default PUK for card unblocking |
KEYCARD_PAIRING_PASSWORD |
Default pairing password for V1 cards |
KEYCARD_NEW_PIN |
New PIN for change-pin and unblock-pin |
KEYCARD_NEW_PUK |
New PUK for change-puk |
KEYCARD_NEW_PAIRING_PASSWORD |
New pairing password for change-pairing-password |
KEYCARD_CARD_CA |
CA public key for V2 certificate verification |
KEYCARD_TEST_CARD |
Use test card CA (boolean) |
KEYCARD_WHITELIST_CARD |
Whitelisted card identity public key |
Show the CLI version.
keycard versionShow card information (applet version, initialization status, public key, etc.).
keycard info
keycard info --jsonInstall applets to the card. Download the cap file from the keycard-tech/status-keycard releases page.
keycard install --applet-file PATH_TO_CAP_FILEBy default, the keycard and ident applets are installed. Control which applets get installed:
| Flag | Default | Description |
|---|---|---|
--keycard-applet |
true |
Install keycard applet |
--ident-applet |
true |
Install ident applet |
--cash-applet |
false |
Install cash applet |
--ndef-applet |
false |
Install NDEF applet |
--force / -f |
Force reinstallation if already installed | |
--ndef <url> |
URL for the NDEF record (supports {{.cashAddress}} variable) |
|
--load-math <path> |
Load the keycard-math-soft library package from this cap file |
The Keycard package depends on the keycard-math-soft library package (AID A0000008040002), which must be loaded on the card. Load it together with the Keycard package:
keycard install --applet-file PATH_TO_CAP_FILE --load-math PATH_TO_MATH_CAP_FILEThe flag is optional: leave it out if the math package is already loaded on the card (it only needs to be loaded once). If the install fails and the math package was not loaded, the CLI reminds you that it might be missing.
keycard delete --yes| Flag | Default | Description |
|---|---|---|
--remove-math |
false |
Also remove the keycard-math-soft library package |
The math package is kept by default, since other applets may depend on it. Pass --remove-math for a full wipe; it is removed after the Keycard package, which depends on it.
Initialize the card and generate the secrets needed for authentication.
keycard initOptions:
| Flag | Description |
|---|---|
--pin <pin> |
Set a specific PIN (random if omitted) |
--puk <puk> |
Set a specific PUK (random if omitted) |
--pairing-password <pw> |
Set pairing password (V1 only; defaults to KeycardDefaultPairing if omitted) |
--alt-pin <pin> |
Set alternative PIN (random if omitted) |
--pin-retries <n> |
Number of PIN retries allowed (default: 3) |
--puk-retries <n> |
Number of PUK retries allowed (default: 5) |
On V2 cards (Secure Channel V2), the pairing password is not used.
keycard factory-reset --yesPairing is only needed for applet versions < 4.0 (Secure Channel V1). V2 cards use certificate-based authentication.
Pair with the card to establish a secure channel.
keycard pair --pairing-password YOUR_PAIRING_PASSWORDUnpair a specific pairing index.
keycard unpair --index 0 --pin YOUR_PINRemove all pairings from the card (including the current session).
keycard unpair-all --pin YOUR_PINGenerate a new random key on the card.
keycard generate-key --pin YOUR_PINGenerate a BIP39 mnemonic phrase using the card's secure RNG. The phrase is returned but not loaded by default. Use --save to also load the mnemonic's seed onto the card.
keycard generate-mnemonic --words 12 # Generate only (no PIN needed)
keycard generate-mnemonic --words 24 --save --pin YOUR_PIN # Generate and load onto card| Flag | Default | Description |
|---|---|---|
--words <n> |
12 |
Number of words: 12, 15, 18, 21, or 24 |
--save |
Also load the mnemonic's binary seed onto the card (requires PIN) |
Remove the current key from the card.
keycard remove-key --pin $KEYCARD_PINDerive a key at the given path (applet < 4.0 only).
keycard derive-key --path "m/44'/60'/0'/0/0" --pin $KEYCARD_PINLoad a BIP39 seed onto the card.
keycard load-seed --mnemonic "word1 word2 ... word12" --pin YOUR_PIN
keycard load-seed --hex "0x..." --pin YOUR_PINLoad a seed for usage with the LEE (Logos Execution Environment) onto the card (applet >= 4.0 only).
keycard load-lee-seed --mnemonic "word1 word2 ... word12" --pin YOUR_PINExport the public key.
keycard export-public-key --path "m/44'/60'/0'/0/0" --pin YOUR_PINExport the private key. Only works for paths in the EIP-1581 tree.
keycard export-private-key --path "m/43'/60'/1581'/4'/1469833213'/1555737549" --pin YOUR_PINExport the extended key (public key + chain code).
keycard export-extended-key --path "m/44'/60'/0'/0/0" --pin YOUR_PINExport a LEE key at the given path (applet >= 4.0 only).
keycard export-lee-key --path "m/44'/60'/0'/0/0" --pin YOUR_PINExport a BIP85 derived key (applet >= 4.0 only).
keycard export-bip85 --path "m/83696968'/39'/0'/12'/0'" --length 16 --pin YOUR_PINCompute an ECDH shared secret between the key derived at the given path and a peer public key (applet >= 4.0 only).
keycard ecdh --peer-key "0x04..." --path "m/44'/1237'/0'/0/0" --pin YOUR_PINSign a 32-byte hash.
keycard sign --hex "0x..." --path "m/44'/60'/0'/0/0" --algo ecdsa --pin YOUR_PIN
keycard sign --hex "0x..." --path "m/44'/60'/0'/0/0" --algo schnorr --pin YOUR_PIN
keycard sign --hex "0x..." --path "m/44'/60'/0'/0/0" --algo schnorr --tweak "0x..." --pin YOUR_PINThe --tweak flag (32 bytes hex) is only valid when --algo schnorr is used and requires --path.
Sign a message using the Ethereum Signed Message format.
keycard sign-message "Hello, Keycard!" --path "m/44'/60'/0'/0/0" --pin YOUR_PIN
keycard sign-message "Hello, Keycard!" --path "m/44'/60'/0'/0/0" --algo schnorr --tweak "0x..." --pin YOUR_PINSign a file (hashes the file content with Keccak256).
keycard sign-file --file /path/to/file --path "m/44'/60'/0'/0/0" --pin $KEYCARD_PIN
keycard sign-file --file /path/to/file --path "m/44'/60'/0'/0/0" --algo schnorr --tweak "0x..." --pin $KEYCARD_PINSet or reset the pinless signing path (applet < 4.0 only).
keycard set-pinless-path --path "m/44'/60'/0'/0/0" --pin $KEYCARD_PIN
keycard reset-pinless-path --pin $KEYCARD_PINSign without PIN verification (applet < 4.0 only). Requires the pinless signing path to have been set.
keycard sign-pinless --hex "0x..."Sign a message without PIN (applet < 4.0 only). Requires the pinless signing path to have been set.
keycard sign-message-pinless "Hello"Identify the card (applet < 4.0 only).
keycard identifyNote: The examples below pass credentials on the command line for clarity. In production, use environment variables (
KEYCARD_PIN,KEYCARD_PUK,KEYCARD_PAIRING_PASSWORD,KEYCARD_NEW_PIN,KEYCARD_NEW_PUK,KEYCARD_NEW_PAIRING_PASSWORD) to avoid exposing secrets in shell history andpsoutput.
Verify the PIN.
keycard verify-pin --pin YOUR_PINChange the PIN.
keycard change-pin --new NEW_PIN --pin YOUR_PINChange the PUK.
keycard change-puk --new NEW_PUK --pin YOUR_PINUnblock the PIN using the PUK.
keycard unblock-pin --puk YOUR_PUK --new-pin NEW_PINChange the pairing password (applet < 4.0 only).
keycard change-pairing-password --new NEW_PASSWORD --pin YOUR_PINRead data from the card.
keycard get-data --type public
keycard get-data --type ndef
keycard get-data --type cashStore data on the card.
keycard store-data --type public --hex "0x..." --pin YOUR_PIN
keycard store-data --type public --file /path/to/data --pin YOUR_PINGet a random challenge from the card (applet >= 4.0 only).
keycard get-challenge --length 32Set the NDEF record on the card.
keycard set-ndef --hex "0x..." --pin YOUR_PIN
keycard set-ndef --file /path/to/ndef --pin YOUR_PINGet card status (PIN retries, key path, etc.).
keycard get-statusGet the card's display name.
keycard get-nameSet the card's display name.
keycard set-name --name "My Keycard" --pin YOUR_PINShow Cash applet information.
keycard cash-infoSign data using the Cash applet.
keycard cash-sign --hex "0x..."Load an identity certificate onto the Ident applet.
keycard load-ident --hex "0x..."
keycard load-ident --testStart a shell session that reads commands from a script file or stdin for batch operations.
The shell is non-interactive — it requires either a script file (-f) or piped input via stdin.
# Run a script file
keycard shell -f script.sh
# Pipe commands from stdin (example: select the applet)
echo "keycard-select" | keycard shell
# JSON output (JSONL)
keycard shell -f script.sh --jsonThe shell supports template functions like {{env "KEYCARD_PIN"}}, {{session_pairing_key}}, {{session_pin}}, etc. See the _shell-commands-examples directory for examples.