Skip to content

Comments

Add keycard support#558

Merged
bdrhn9 merged 17 commits intomainfrom
keycard-support-poc
Jan 23, 2026
Merged

Add keycard support#558
bdrhn9 merged 17 commits intomainfrom
keycard-support-poc

Conversation

@bdrhn9
Copy link
Contributor

@bdrhn9 bdrhn9 commented Nov 16, 2025

Summary

This PR adds support for Keycard hardware wallet as a wallet derivation scheme option, enabling secure transaction signing using a physical smart card.

Breaking changes ⚠️

This should be released as a major due to schema changes.

The configuration schema has been restructured:

  • sponsorWalletMnemonic has been moved from a top-level config field into walletDerivationScheme
  • Each wallet derivation type (self-funded, managed, fixed) now includes its own sponsorWalletMnemonic field
  • New keycard type added which uses pin instead of mnemonic

Before:

{
  ...
  "sponsorWalletMnemonic": "test test test...",
  "walletDerivationScheme": { "type": "managed" }
  ...
}

After:

{
  ...
  "walletDerivationScheme": {
    "type": "managed",
    "sponsorWalletMnemonic": "test test test..."
  }
  ...
}

This restructuring improves the schema design as sponsorWalletMnemonic and walletDerivationScheme were inherently related and are now logically grouped together.

Changes

  • Keycard integration: Added new keycard wallet derivation scheme type that accepts a PIN for hardware wallet authentication
  • Batch transaction support: Keycard deployments utilize batch transaction submitting for efficient signing
  • Plugin architecture: Implemented keycard.ts as a plug-in component (similar to env.ts) for clean separation of concerns. This design makes it easier to understand the flow and serves as a reference implementation for using keycard-manager within the organization
  • Lifecycle management: Added proper initialization and termination of keycard connection during startup/shutdown
  • Dynamic import: keycard-manager is dynamically imported only when needed to avoid loading it for non-keycard deployments

Design decisions

Heartbeat logging: Heartbeat logging is disabled when keycard is the walletDerivationScheme. This is intentional as heartbeat logging is designed to monitor Cloud deployments, while keycard-based deployments are intended for local use. Open to discussion if heartbeat logging should also be supported for keycard deployments.

State management: The keycard wallet is managed in keycard.ts as a module-level variable rather than in state.ts. This plug-in approach was preferred for clarity, though moving it to state.ts is also a viable alternative.

Tested scenarios

  • Verifying existing wallet derivation schemes (self-funded, managed, fixed) work with new schema structure
  • Testing keycard initialization and transaction signing with physical Keycard
  • Verifying batch transaction processing works correctly with keycard
  • Confirming heartbeat logging is properly skipped for keycard deployments
  • Ensuring graceful shutdown terminates keycard connection

@bdrhn9 bdrhn9 marked this pull request as ready for review January 21, 2026 13:11
@bdrhn9 bdrhn9 requested a review from hiletmis January 21, 2026 13:12
Copy link

@hiletmis hiletmis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the seamless integration.

I have run a test with keycard on bsc-testnet using a config from airseeker-admin by just changing managed to keycard and providing a PIN.

As PIN is mentioned, it can be optional either by not providing on config (N/A ATM) or not defining in secrets.env. Either way it will work with keycard as keycard-manager will eventually asks for PIN to proceed.

Ofc to avoid delays or allow auto-restarts PIN can be hardcoded.

Hope keycard bring peace and happiness to Airseeker

The PIN for the Keycard hardware wallet. Required only when `type` is set to `keycard`. For example:

```jsonc
"walletDerivationScheme": { "type": "keycard", "pin": "${KEYCARD_PIN}" },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding following line to secrets.example.env can be considered

KEYCARD_PIN=000000

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intentionally didn't include this environment variable (afbe354) because it isn't included in the airseeker.example.json template.

@bdrhn9
Copy link
Contributor Author

bdrhn9 commented Jan 23, 2026

As PIN is mentioned, it can be optional either by not providing on config (N/A ATM) or not defining in secrets.env. Either way it will work with keycard as keycard-manager will eventually asks for PIN to proceed.

That’s a good point. I hadn't considered the interactive prompt since the goal was to run this in a detached Docker container. I’ll update the schema now to support cases where the PIN isn't defined.

@bdrhn9 bdrhn9 merged commit fbfa627 into main Jan 23, 2026
5 checks passed
@bdrhn9 bdrhn9 deleted the keycard-support-poc branch January 23, 2026 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants