Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: 'Batch poster: External signing (KMS)'
sidebar_label: 'KMS and signing services'
description: 'Learn how to integrate external signing—including AWS KMS for the batch poster for your chain.'
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated
author: pete-vielhaber
sme: jason-w123
user_story: As an Arbitrum chain operator, I want to integrate KMS and signing services for my batch poster.
content_type: how-to
---

Nitro's batch poster (and staker) sign their L1 transaction through the **DataPoster** component. By default it signs locally with a private key, but it also supports **generic RPC-based external signing**: instead of holding the key, Nitro sends an unsigned transaction to a remote signer over (m)TLS, gets back a signed transaction, and independently verifies it.
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated

:::info

There is **no native AWS KMS integration** in the Nitro codebase. KMS support is achieved by running a _separate signer service_ that talks to KMS and exposes an Ethereum-style `eth_signTransaction` RPC endpoint. Nitro connects to that endpoint. In other words: "KMS support" = external signer pointed at a KMS-backed signing service.

:::

## How it works internally

1. **Connect**—`rpcClient()` dials the signer URL with a TLS config: optional client cert/key for mTLS (`ClientCert`/`ClientPrivateKey`), optional `RootCA` (lets you use self-signed certs), and `InsecureSkipVerify`.
2. **Sign**—`externalSigner()` returns a signer callback that:

- Converts the transaction to `apitypes.SendTxArgs` via `TxToSignTxArgs`
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated
- Note that it fully supports EIP-4844 blob transactions (blobs, commitments, proofs), which the batch poster needs.
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated
- Calls the configured RPC method: `client.CallContext(ctx, &data, opts.Method, args)`, expecting an RLP-encoded signed transaction back.
- **Verifies** the returned transaction: the hash must match the request and the recovered sender must equal the configured `Address`. This means TLS is _not_ relied on for authentication—the signature itself is checked at the application layer.

## Configuration

The config struct is `ExternalSignerCfg`:

| Field | koanf key | Purpose |
| -------------------- | ---------------------- | -------------------------------------------------------------------------------------------- |
| `URL` | `url` | RPC endpoint of the signer. **Setting this enables external signing** (overrides local key). |
| `Address` | `address` | Hex Ethereum address the signer controls; used to verify returned signatures. |
| `Method` | `method` | RPC method name, e.g., `eth_signTransaction`. |
| `RootCA` | `root-ca` | (Optional) CA cert to trust — enables self-signed server certs. |
| `ClientCert` | `client-cert` | (Optional) client cert for mTLS. |
| `ClientPrivateKey` | `client-private-key` | (Optional) client key for mTLS (required if `client-cert` set). |
| `InsecureSkipVerify` | `insecure-skip-verify` | Skip server TLS verification (not recommended). |

This config is nested under both the batch poster and the staker. So the full CLI flag paths are:
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated

**Batch poster:**

```
--node.batch-poster.data-poster.external-signer.url
--node.batch-poster.data-poster.external-signer.address
--node.batch-poster.data-poster.external-signer.method
--node.batch-poster.data-poster.external-signer.root-ca
--node.batch-poster.data-poster.external-signer.client-cert
--node.batch-poster.data-poster.external-signer.client-private-key
--node.batch-poster.data-poster.external-signer.insecure-skip-verify
```

**Staker** uses the same fields under `--node.staker.data-poster.external-signer.*`
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated

When `external-signer.url` is empty, the batch poster requires a local key. The check in `cmd/nitro/nitro.go` confirms this—and note that **AnyTrust mode still needs a local key** (external signing isn't supported alongside AnyTrust there).
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated

## What the signer service must implement

Your signer (whether KMS-backed or otherwise) must expose an HTTPS RPC server with a method matching `Method`, that:
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated

- Accepts an Ethereum transaction object (`apitypes.SendTxArgs`—the standard `eth_signTransaction` shape, including blob fields for EIP-4844),
- Returns the RLP-encoded **signed** transaction as a hex string,
- Signs with the key for the address you configured as `address`.

If you use mTLS, the server must require and verify the client cert that matches `client-cert`/`client-private-key`.

## Reference implementations

Nitro ships two working examples you can model a KMS service on:

- **`cmd/mockexternalsigner/mockexternalsigner.go`**—a standalone signer binary. It
builds an `rpc.Server`, registers a signing method, serves over HTTPS with
`tls.RequireAndVerifyClientCert`, and prints the exact flags to pass to Nitro. Swap the
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated
local-key `txOpts.Signer` for a KMS-backed signer and you have a KMS integration.
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated
- **`arbnode/dataposter/externalsignertest/externalsignertest.go`**—the test harness,
showing the server side (`SignerAPI`, method registration, cert setup with
`RequireAndVerifyClientCert`). The RPC method (`externalsignertest.go:185`) takes
`*apitypes.SendTxArgs` and returns the RLP-encoded signed tx as `hexutil.Bytes`.
Comment thread
pete-vielhaber marked this conversation as resolved.
Outdated
5 changes: 5 additions & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,11 @@ const sidebars = {
id: 'launch-arbitrum-chain/integrations/infrastructure-providers',
label: 'Infrastructure providers',
},
{
type: 'doc',
id: 'launch-arbitrum-chain/integrations/bp-kms-signing-services',
label: 'KMS signing services',
},
],
},
{
Expand Down
Loading