Skip to content

Comments

Add signer plugins#2400

Draft
mootz12 wants to merge 7 commits intomainfrom
add-signer-plugins
Draft

Add signer plugins#2400
mootz12 wants to merge 7 commits intomainfrom
add-signer-plugins

Conversation

@mootz12
Copy link
Contributor

@mootz12 mootz12 commented Feb 18, 2026

What

Add signer plugins that can be invoked during the transaction submission process to add arbitrary logic while signing authorization entries or transaction envelopes.

Why

Writing custom code to support signing for non-standard account types is not well supported today. A few methods have emerged:

  1. Write plugins that can be used in nested commands apply signatures (stellar-cli-sign-auth-ed25519)
  2. Write plugins that build, sign, and submit the transaction on their own (smart-account-sign)

Having an integration point within the transaction submission process would simplify the process of writing, sharing, and using plugins made for signing. Further, these could be supported by future plugins as well, like protocol specific ones that help interact with protocols, e.g. stellar-private-payments.

Closes: #2026

How

NOTE: CLI API under construction. The current version is just a proof of concept that writing the plugin is reasonable. Please provide feedback!

Two new arguments are added to sign_with.rs to allow users to specify singing plugins for a given Address/Alias.

  • --sign-with-plugin - Takes in a plugin_name=Address that ensures each time Address is required to sign, the given plugin with plugin_name is invoked, such that the plugin is an executable at stellar-signer-{plugin_name}. Multiple relationships can be created.
  • --plugin-arg - Takes in a string of plugin_name:arg_name=value, such that each time plugin_name is called, in the arg json string provided, { arg_name: value } is included.

Plugins need to support two different signing methods, each with a different JSON-string provided as an argument.

Sign Authorization Entries

  • Input:
{
  "mode": "sign_auth",
  "network_passphrase": "Test...",
  "address": "ScAddress as a Base64 XDR string",
  "nonce": 0,
  "signature_expiration_ledger": 100,
  "root_invocation": "SorobanAuthorizedInvocation as a Base64 XDR string",
  "args": {// based on --plugin-arg fields}
}
  • Output: Base64 XDR string of the ScVal representing the credential signature for the auth entry

Sign Transaction

  • Input:
{
  "mode": "sign_tx",
  "network_passphrase": "Test...",
  "tx_env_xdr": "TransactionEnvelope as a Base64 XDR string",
  "tx_hash": "hex encoded tx_hash",
  "args": {// based on --plugin-arg fields}
}
  • Output: JSON array of base64 XDR strings`representing DecoratedSignatures to add to the transaction envelope

Try It Out

An example signer plugin was created for Stellar multisig accounts (cmd/crates/stellar-multisig-plugin). The same plugin was also created for comparison purposes that works with the existing CLI, using nested commands (cmd/crates/stellar-old-multisig-plugin).

Setup CLI

  • Checkout this branch!

  • Install Stellar CLI:

make
make install
  • Install plugins:
make use-signing-plugin
make use-old-signing-plugin

Setup test environment

  • Create two funded stellar accounts on a standalone or test network. 1 should be a multisig where the master key cannot submit a transaction (alice in this example). 1 should be a regular stellar account (bob in this example).

  • Deploy the hello-world contract:

stellar contract --source bob upload --wasm ./target/wasm32v1-none/test-wasms/test_hello_world.wasm

Custom Signing for Source Account

  • Using the singing plugin:
stellar contract invoke \ 
  --source alice 
  --sign-with-plugin multisig=alice \
  --plugin-arg multisig:signers=S..,S.. \ 
  --id C... \
  -- auth --addr alice --world Test1
  • Using the nested commands:
stellar contract invoke --source alice --id C.. --build-only -- auth --addr alice --world TestOld \
  | stellar tx simulate \
  | stellar sign-auth-multisig \
      --signers S..,S.. \
      --signature-expiration-ledger 102345 \
      --network-passphrase 'Standalone Network ; February 2017' \
      --sign-tx \
  | stellar tx send

Custom Signing for Authorization Entries

  • Using the singing plugin:
stellar contract invoke \ 
  --source samwise 
  --sign-with-plugin multisig=alice \
  --plugin-arg multisig:signers=S..,S.. \ 
  --id C... \
  -- auth --addr alice --world Test1
  • Using the nested commands:
stellar contract invoke --source samwise --id C.. --build-only -- auth --addr alice --world TestOld \
  | stellar tx simulate \
  | stellar sign-auth-multisig \
      --signers S..,S.. \
      --signature-expiration-ledger 102345 \
      --network-passphrase 'Standalone Network ; February 2017' \     
  | stellar tx simulate \
  | stellar tx sign --sign-with-key samwise \
  | stellar tx send

TODO

  • Explore allowing "key/contract" config to store signing plugin data and arguments. That is, you would setup signing plugins directly on the key/contract itself, stellar keys use-plugin alice --name multisig -- --signers S..,S.., preventing the need for the weird plugin arg CLI API to be used. This is likely to touch a lot of code, so was omitted until we decide on a direction

  • Support different arguments based if more than one address uses the same plugin. Currently, the same arguments are passed to all invocations of a singing plugin.

  • General code polishing / testing

  • Remove plugin examples from CLI repo

@github-project-automation github-project-automation bot moved this to Backlog (Not Ready) in DevX Feb 18, 2026
@leighmcculloch
Copy link
Member

Thanks for thinking about this. Some thoughts:

  • config to store signing plugin data and arguments

The ability to store the signing plugins config for a key is where a ton of value is.

Without it the --sign-with-plugin doesn't offer a significant advantage over stellar sign-auth-multisig. There is the advantage of not having to simulate twice, which is not intuitive today. But otherwise the flow is similar, the learning curve is similar, with some additional limitations due to the synchrony (see last paragraph).

But, if we add the ability to store these plugin configs alongside keys, that flow could be pretty amazing. So I think it is worth hashing that out as part of this proof-of-concept because that's where some of the value will be.

  • Support different arguments based if more than one address uses the same plugin.

+1

Another thing to consider is: can a plugin utilise the key storage of the cli. The examples above hint that maybe that should be possible. In the multisig example where it has multisig:signers=S..,S.., it would be convenient if those keys could be managed by the stellar keys command, maybe they get stored in a hardware device, and then they could be conveniently referenced via an alias as well.

Another thing to consider is: how should a plugin that requires asynchronous / detached signing work. That is something the current piping flow naturally supports today because you can build, simulate, then send the tx to someone else, then get it back and continue. The composability and asynchrony is particularly powerful.

@mootz12
Copy link
Contributor Author

mootz12 commented Feb 19, 2026

Another thing to consider is: how should a plugin that requires asynchronous / detached signing work

The example doesn't show this, but the CLI will wait for the singing plugin process to terminate, and it does attach stderr so the plugin process can send prompts / URLs / etc to the user.

The ability to store the signing plugins config for a key is where a ton of value is.

I agree. I can explore this next.

Another thing to consider is: can a plugin utilise the key storage of the cli.

We could export this logic as a standalone crate, in which case they could. Otherwise they would need to duplicate the code.

IMO if we allow keys/contract aliases to store singing plugin info, this wouldn't be needed. One thing we could consider here is making signing plugins a bit more CLI like.

E.g. they have to support the following:

stellar-signer-plugin sign-auth --payload {JSON string payload} --my-arg S..
stellar-signer-plugin sign-tx --payload {JSON string payload} --my-arg S...

Then, plugins could define their own "set-args" logic so ppl have help text, using the stellar CLI under the hood.

stellar my-plugin use --address (G../M../C../alice) --my-arg S...
-> invokes stellar keys use-plugin (G../M../C../alice) -- my-plugin --my-arg S...

@leighmcculloch
Copy link
Member

Another thing to consider is: how should a plugin that requires asynchronous / detached signing work

The example doesn't show this, but the CLI will wait for the singing plugin process to terminate, and it does attach stderr so the plugin process can send prompts / URLs / etc to the user.

This flow will work for short lived asynchronous signing, but not asynchronous signing.

But, as long as the signing plugins can also be used with stellar tx sign, that should make it possible to use them asynchronously.

@leighmcculloch
Copy link
Member

Another thing to consider is: can a plugin utilise the key storage of the cli.

Another option is to expose the stellar cli binary path to the sub-process via an environment variable so the sub-process knows how to call the stellar binary to use the keys command.

For example, like how inside a Makefile you can use $(MAKE) to call the make command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog (Not Ready)

Development

Successfully merging this pull request may close these issues.

Explore a general way to build signing plugins

2 participants