Skip to main content
Clef is a standalone signing tool included with go-ethereum. It manages keys and signs transactions as a separate process, so Geth never has direct access to private keys. All signing requests from Geth (or any other caller) are forwarded to Clef over IPC or HTTP, where they are logged, validated against an optional rules engine, and either auto-approved or presented to the user for manual confirmation.

Why use Clef

  • Process isolation — private keys live only inside the Clef process; Geth cannot read them.
  • Audit log — every signing request is written to an append-only audit log (audit.log by default).
  • Rules-based signing — a JavaScript rules file can auto-approve routine requests without user interaction.
  • Credential storage — account passwords are stored in Clef’s encrypted vault, not passed on the command line.

Initial setup

1

Initialize Clef

Before first use, Clef must generate a master seed used to encrypt its internal storage (credentials, rule hashes, configuration).
clef init
You will be prompted to set a master password. The seed is written to ~/.clef/masterseed.json.
Back up masterseed.json and remember the master password. This seed protects all credentials stored by Clef. It does not contain your account keys — back those up separately from the keystore directory.
2

Start Clef

Point Clef at your keystore directory and specify the chain ID:
clef --keystore ~/.ethereum/keystore --chainid 1
Common chain IDs:
NetworkChain ID
Mainnet1
Holesky17000
Sepolia11155111
Hoodi560048
On startup, Clef opens an IPC endpoint (default ~/.clef/clef.ipc) and prints connection details:
INFO  IPC endpoint opened  url=$HOME/.clef/clef.ipc
3

Connect Geth to Clef

Pass the Clef IPC socket path to Geth using the --signer flag:
geth --signer ~/.clef/clef.ipc
From this point on, Geth routes all account operations (listing, signing) to Clef. Geth itself holds no keys.

Clef operations

Account listing

List all accounts in the keystore from the Clef CLI:
clef list-accounts --keystore ~/.ethereum/keystore
Or send a raw JSON-RPC request to the IPC socket:
echo '{"id": 1, "jsonrpc": "2.0", "method": "account_list"}' | nc -U ~/.clef/clef.ipc
Clef prompts the user to confirm the listing and select which accounts are visible to the caller. On approval:
{"jsonrpc":"2.0","id":1,"result":["0xd9c9cd5f...","0x086278a6..."]}

Signing transactions

When a connected application (such as Geth) requests a transaction signature, Clef displays the transaction details and asks for approval:
--------- Transaction request ---------------
to:     0xae967917c465db8578ca9024c205720b1a3651a9
value:  1000000000000000 wei
gas:    0x55730 (350000)
gasPrice: 4000000000 wei
---------------------------------------------
Approve? [y/N]:
The signed transaction is returned to the caller only after the user approves.

Signing messages (personal_sign)

Clef handles personal_sign requests in the same way — the message content and signing account are displayed before the user is asked to approve.

The rules system

For automated workflows, Clef supports a JavaScript rules file. Each signing request invokes a corresponding JS function. The function can return:
  • "Approve" — auto-approve without prompting the user.
  • "Reject" — auto-reject without prompting the user.
  • Anything else (or an error) — forward to the user for manual confirmation.
The rules engine has access to a persistent storage object for keeping state between invocations, and a console object for logging.

Example: auto-approve account listing

function ApproveListing() {
    return "Approve"
}

Example: approve transfers below a weekly limit

function big(str) {
    if (str.slice(0, 2) == "0x") {
        return new BigNumber(str.slice(2), 16)
    }
    return new BigNumber(str)
}

// 1 ether weekly limit
var window = 1000 * 3600 * 24 * 7;
var limit  = new BigNumber("1e18");

function isLimitOk(transaction) {
    var value      = big(transaction.value);
    var windowstart = new Date().getTime() - window;
    var stored     = storage.get('txs');
    var txs        = stored != "" ? JSON.parse(stored) : [];
    var recent     = txs.filter(function(tx) { return tx.tstamp > windowstart; });
    var sum        = recent.reduce(function(agg, tx) { return big(tx.value).plus(agg); }, new BigNumber(0));
    return sum.plus(value).lt(limit);
}

function ApproveTx(r) {
    if (isLimitOk(r.transaction)) {
        return "Approve";
    }
    return "Reject";
}

// Track approved transactions for rate limiting
function OnApprovedTx(resp) {
    var value  = big(resp.tx.value);
    var stored = storage.get('txs');
    var txs    = stored != "" ? JSON.parse(stored) : [];
    txs.push({ tstamp: new Date().getTime(), value: value });
    storage.put("txs", JSON.stringify(txs));
}

Attesting a rule file

Clef will not load a rules file unless its SHA-256 hash has been explicitly registered (“attested”) in the secure configuration store. This prevents an attacker who can write files to disk from silently replacing your rules.
1

Compute the hash of your rules file

sha256sum rules.js
Example output:
645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c  rules.js
2

Attest the hash with Clef

clef attest 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c
Clef asks for the master password and then stores the hash:
INFO  Ruleset attestation updated  sha256=645b58e4f...
3

Start Clef with the rule file

clef --keystore ~/.ethereum/keystore --chainid 1 --rules rules.js
You must re-attest any time the rule file is modified.
Every time you edit rules.js you must run clef attest <new-hash> before Clef will accept the updated file. This is intentional: it ensures you explicitly acknowledge every change to the signing policy.

Storing account passwords

For rules that need to auto-sign transactions (not just list accounts), Clef must be able to decrypt the key files without user input. Store the account password in Clef’s encrypted vault:
clef setpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3
Clef prompts for the account password and then for the master password to seal it in the vault. The stored password is used automatically by the rules engine. To remove a stored password:
clef delpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3

Security model

Clef’s security properties depend on process isolation:
  • Geth communicates with Clef only through the IPC socket (or HTTP). It cannot call into Clef’s internal memory.
  • Private keys are decrypted inside the Clef process only when a signing request is being processed, and are not retained in memory after use.
  • The audit log records every request and response, giving an independent record of all signing activity.
  • Rule file attestation ensures that the JavaScript policy code cannot be changed without the operator’s explicit knowledge.
Clef exposes a deliberately narrow External API to callers such as Geth. A richer Internal API is available for building custom UI integrations (for example, a GUI that communicates with Clef over stdio).

Build docs developers (and LLMs) love