- Go 97.4%
- Makefile 1.9%
- Dockerfile 0.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Keep existing release assets and upload only missing files. Leave published release metadata untouched and finalize a draft only after its assets exist. Add a manual metadata-only check for an existing release tag. Require the expected assets, stable release and asset timestamps, and an unchanged tag without rebuilding or downloading binaries. Validation: fresh and repeated publishing, incomplete releases, API failures, old-code regression, 25 verifier cases per repository, and read-only checks of the current published releases. |
||
| .agents/skills/icu-evm-signer | ||
| .forgejo/workflows | ||
| cmd/icu-evm-signer | ||
| internal | ||
| .dockerignore | ||
| .git-allowed-signers | ||
| .gitignore | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| minisign.pub | ||
| README.md | ||
ICU EVM Signer
A non-interactive, offline EVM transaction signer for agent workflows. It reads a complete unsigned transaction, an encrypted Web3 keystore, and a protected secret reference; it returns signed transaction bytes and a decoded preview. It has no RPC client and cannot broadcast.
Build
make build
Builds use Go 1.26.8 and identify local development builds as 0.1.2-dev.
Store credentials
Each keystore must have its own unique secret. Keep both files outside Git in
an owner-controlled directory with mode 0700. The keystore and secret must be
regular files owned by the signing user with mode 0400 or 0600.
Create a one-line secret file without putting the value in shell history:
install -d -m 0700 /secure/path/account
umask 077
IFS= read -r -s -p "Keystore secret: " signer_secret
printf '\n'
printf '%s' "$signer_secret" > /secure/path/account/secret
unset signer_secret
chmod 0600 /secure/path/account/secret
Never place the secret in an argument, environment variable, .env file,
prompt, log, agent message, or RPC/MCP input. Agents may reference credential
paths but must not read their contents.
For an existing protected KEY=value file, use --secret-env-file PATH --secret-env-name KEY; parsing stays inside the signer. Duplicate occurrences
of the selected key are rejected, including empty entries.
Verify a keystore-secret pairing without signing:
icu-evm-signer verify \
--keystore /secure/path/account/keystore.json \
--secret-file /secure/path/account/secret
Sign a transaction
Pass one complete JSON object through stdin or --tx-file:
{
"type": 2,
"chainId": 369,
"from": "0x...",
"nonce": 7,
"to": "0x...",
"valueWei": "0",
"gas": 150000,
"data": "0x...",
"maxFeePerGasWei": "600000000000",
"maxPriorityFeePerGasWei": "1000000000",
"accessList": []
}
Types 0, 1, and 2 are supported. Wei values are unsigned decimal
strings. to may be null for contract creation. The requested from must
match the selected keystore. Duplicate JSON fields, including case variants
and escaped names, are rejected in the transaction and access-list entries.
icu-evm-signer sign \
--keystore /secure/path/account/keystore.json \
--secret-file /secure/path/account/secret \
< unsigned-tx.json
An inherited descriptor keeps the secret path out of process arguments:
icu-evm-signer sign \
--keystore /secure/path/account/keystore.json \
--secret-fd 3 \
3</secure/path/account/secret \
< unsigned-tx.json
Success returns one JSON object containing rawTx, txHash, and a transaction
preview. The preview includes the complete calldata in data and all
accessList entries and storage keys; empty values are "0x" and [].
Compare every transaction field with the intended request before relay.
Only rawTx should cross into a gas-gated broadcast tool. Receipt
status and canonical block membership must be checked before reporting
execution.
Exit code 2 means invalid usage or transaction input, 3 means credential
access or decryption failed, and 4 means signing or encoding failed.
Sign an EIP-191 message
sign-message signs the input bytes exactly as an Ethereum personal_sign
message and returns the signer address, signature, and prefixed message hash.
icu-evm-signer sign-message \
--keystore /secure/path/account/keystore.json \
--secret-fd 3 \
--message-file challenge.txt \
3</secure/path/account/secret
Container
docker build -t icu-evm-signer .
docker run --rm --network none --read-only \
--user "$(id -u):$(id -g)" \
--mount type=bind,src=/secure/path/account,dst=/run/keys,readonly \
icu-evm-signer sign \
--keystore /run/keys/keystore.json \
--secret-file /run/keys/secret \
< unsigned-tx.json
Development
make check
The gate runs formatting, vet, static analysis, vulnerability scanning, unit and race tests, a native build, a container build, and a binary smoke test.
License
MIT — see LICENSE.