
Sigil
io.github.cdrn由 GitHub 帳號 cdrn 發布v0.0.6更新於 Oct 24, 2026
Local keystore + MCP server. Claude can sign EVM transactions but never sees the private key.
概覽
本機金鑰庫與簽章 MCP 伺服器,讓助理在不接觸私鑰的情況下簽署 EVM 與 Solana 交易和訊息。
- 可以做什麼
- 簽署 EVM 訊息與交易:EIP-191 personal_sign、EIP-1559 與舊式交易、EIP-712 型別化資料,皆透過不透明金鑰代號完成。
- 簽署 Solana 資料:ed25519 訊息簽章以及序列化交易訊息的簽章。
- 依入口執行政策:鏈 ID、目的地位址與函式選擇器白名單、單筆金額上限、每小時/每日滾動額度,以及需人工確認的簽章。
- 透過 ntfy 在大額簽章前要求帶外人工核准。
- 為 Foundry 與 Hardhat 等工具提供本機 JSON-RPC 簽章端點。
- 適用情境
- 當代理式編碼工具需要簽署或廣播鏈上交易,而你希望私鑰不進入模型上下文時使用。適合測試網機器人、腳本化部署,以及用政策檔限制代理可簽內容的情境。
- 執行需求
- 以本機程序方式透過 npx 從 npm 套件 sigild 啟動;需要 macOS 或 Linux 上的 Node 22+(不支援 Windows)。簽章前須在另一個終端以 sigil unlock 命令列推送通關密語。選用環境變數 SIGIL_HOME 與 SIGIL_CONTROL_SOCK 可覆寫預設的 ~/.sigil 路徑。帶外確認需要 ntfy 主題;JSON-RPC 代理需要上游節點網址與產生的權杖。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Sigil,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
sigil
Claude can sign, but never see.
sigil is a local signing tool and Claude Code integration that lets agentic coding tools use private keys without ever putting key material in the model's context window.
Status: pre-alpha. The MCP server, CLI, unlock flow, ward hooks, policy engine (static checks), out-of-band confirmation via ntfy, Solana signing, and the JSON-RPC signing proxy (Foundry/Hardhat) all work end-to-end. Rolling-window value caps and EIP-712 domain allowlists landed in the policy engine, but have not been through a release yet. Until v0.1.0 ships — with the supply-chain attestations promised for it — do not use this with real funds yet. Build plan lives in the tracking issue.
What it is
One MCP server process, four bins (plus a legacy sigild alias), six runtime deps (all pinned, zero transitive):
sigil-mcp— the only thing that runs. Claude Code spawns it per session via yourmcpServersconfig; it dies when Claude exits. Holds unlocked keys in process memory (zeroized on shutdown,sigil lock, or unlock-failure; mlock against swap is planned). Keys at rest are encrypted with XChaCha20-Poly1305 and an Argon2id-derived key. Signs over stdio using a DIY MCP wire protocol (~200 lines, no SDK dep). Claude never sees key material — only opaque handles likeevm:executor.sigil— control CLI.init,status,portal new/add/list/qr/remove,policy show/init,unlock,lock.sigil-hook-pre/sigil-hook-post— Claude Code hook binaries that block reads of common key paths and redact key-shaped strings from tool output.
sigil-mcp boots locked: empty in-memory handle table, no keys loaded. Sign methods return DAEMON_LOCKED (-32003) with a "run sigil unlock" message until you push the passphrase in from a separate terminal via sigil unlock. That CLI connects to a per-session Unix socket at ~/.sigil/control/<pid>.sock (0600) that sigil-mcp opens at startup — and fans out to every such socket so one sigil unlock reaches all open windows. After unlock, signs work for the rest of the session; sigil lock zeroizes the table without killing the process.
Sign methods exposed today: EIP-191 personal_sign, EIP-1559 + legacy transactions, EIP-712 typed data, plus Solana (ed25519) message + transaction signing — see Solana support below.
What it isn't
- Not a hardware wallet replacement. If you can use a Ledger or YubiKey, do that.
- Not a custody solution. It runs on your laptop or VPS and protects you from one specific class of failure: leaking key material through an LLM agent.
- A first cut of bounding signing authority via the policy engine — but not the full thing. Shipped: static checks (chain ID, destination allowlist, per-tx value cap, function-selector allowlist, contract-creation gating, on/off toggles for personal_sign and EIP-712) plus out-of-band human confirmation via ntfy. Rolling-window caps and EIP-712 domain allowlists are tracked in #3 and will land incrementally.
Install
This drops four binaries on your $PATH: sigil, sigil-mcp, sigil-hook-pre, sigil-hook-post (plus sigild, a legacy alias for sigil-mcp). (The package name on npm is sigild for legacy reasons; the bins do not include a daemon any more.)
Requires Node 22+, macOS or Linux. (The CLI ↔ session control channel uses Unix domain sockets; Windows is untested and currently unsupported.)
Quick start
If you close Claude Code, sigil-mcp exits and its memory is wiped. Open a new session and sigil unlock again — the encrypted keyfiles on disk persist.
CLI reference
Set SIGIL_HOME to override ~/.sigil. Set SIGIL_CONTROL_DIR to override the control-socket directory.
Multi-window behaviour
Each Claude Code window spawns its own sigil-mcp, and each binds its own control socket at ~/.sigil/control/<pid>.sock. They share the on-disk keyfiles + audit log but keep separate in-memory handle tables.
sigil unlock / lock / status fan out across every socket in ~/.sigil/control/, so a single sigil unlock loads keys into all currently-open windows — no more guessing which process the CLI reaches. Sockets left behind by hard-killed sessions are detected and cleaned up automatically on the next CLI call.
Each window still holds its own decrypted keys only for its own lifetime: closing a window zeroizes that session's keys, and a window opened after you unlock starts locked (run sigil unlock again to include it). Keys never outlive the Claude sessions that use them — a deliberate property from #23.
OS-keychain integration (planned, v0.3) will make unlock zero-touch for users who set it up.
After upgrading sigil, restart every Claude window's sigil-mcp (reconnect the MCP server or reopen the window, then sigil unlock again). A running sigil-mcp never reloads code, and daemons from different versions must not share ~/.sigil/audit.log: the lock protocol and the writer's chain checks are version-specific. ps -ax | grep sigil-mcp shows stragglers.
Policy engine
Once a portal is unlocked, signing authority over its key is real. To bound the blast radius of a successful prompt injection, every portal has a policy file at ~/.sigil/policy/<handle>.toml. Two modes:
Permissive (default for sigil portal add): no rules. Sign anything the agent asks. The key isolation guarantees still hold — your key never enters the agent's context — but the unlocked portal can be made to sign whatever an attacker can get the agent to ask for. Useful for: testnet bots, demo flows, anyone who only cares about the context-window protection.
Strict (opt in with --strict): every sign request is checked. Generated template:
A failed rule throws POLICY_DENIED (-32001) back to the agent with the human-readable reason ("tx denied — value X exceeds max_value_wei Y"), and the deny is appended to the hash-chained audit log alongside allows. Denies are forensically the more interesting half — they're the prompt-injection canary.
Rolling-window caps (max_value_per_hour_wei, max_value_per_day_wei, and svm_max_lamports_per_hour/day) are the one rule that applies in both modes — a permissive "bot with an allowance" is the common case. They count native value only: tx.value for EVM, decoded System-Program transfers for Solana. ERC-20 / SPL transfers are calldata, not native value, and are not counted — and require_confirm_above_wei doesn't see them either (a token transfer has value = 0). Gate token movement with strict mode's allow_to + allowed_selectors; for Solana, anything sigil can't decode as a System-Program transfer is routed to confirm in strict mode, and denied outright when a lamport cap is set. Spends are recorded in a per-portal ledger at ~/.sigil/state/<handle>.ledger (append-only JSONL, shared across windows through the same lock as the audit log, compacted automatically) and the check-and-record is atomic, so two windows can't both take the last of an allowance. The cap is checked before any confirm push (so you're not asked to approve something that would be denied anyway) and again right before signing. sigil policy spend <handle> shows the trailing 1h/24h totals against the caps. sigil portal remove deletes the ledger with the portal.
EIP-712 allowlists (strict mode, with allow_typed_data = true): typed_data_verifying_contracts and typed_data_primary_types each restrict one axis when non-empty, and the domain must carry a chainId that names one of chain_ids (strict mode refuses chain-less domains, since they can't be checked). A typed-data signature can move funds (Permit, Permit2, exchange orders), so it gets the same treatment as a transaction's destination.
Still deferred (in #3): decoded-calldata argument checks.
Out-of-band confirm
For sign requests above require_confirm_above_wei, sigil pushes a notification to a channel you control (not the agent) and waits for an explicit human ack before signing. Today the only transport is ntfy — zero-setup, no accounts. SMS and Telegram transports are wired behind the same ConfirmTransport interface and will land in follow-ups.
Wire it up in ~/.sigil/config.toml:
Install the ntfy app on your phone, subscribe to that topic, and you'll get a push with Approve / Deny buttons every time the threshold is crossed. The buttons hit a local 127.0.0.1 listener inside sigil-mcp with a one-time, request-bound token — a leaked or replayed token can't approve a different sign. Timeout, deny click, and push-provider outage all fail closed.
If any policy file sets require_confirm_above_wei but no transport is configured, sigil-mcp refuses to start with a clear error rather than silently degrading every confirm-gated sign to a deny.
JSON-RPC signing proxy (Foundry / Hardhat / any web3 tool)
sigil can expose a local JSON-RPC endpoint that makes any portal a drop-in signer for tooling that expects an unlocked node account — the same pattern Clef and web3signer use. Contract bytecode goes from forge straight into sigil; it never transits the agent's context or an MCP tool parameter.
Enable it with one command (generates the token, writes the config block, prints the forge invocation):
...or by hand in ~/.sigil/config.toml:
Then point any tool at it, with the token as the Basic-auth password:
The proxy serves three things with the portal key and forwards everything else (eth_call, eth_estimateGas, eth_getTransactionReceipt, …) to the upstream:
eth_accounts→ the portal address (empty while locked)eth_signTransaction→ fills nonce/gas/fees if missing, signs, returns the raw txeth_sendTransaction→ same, then broadcasts via the upstream and returns the hash
Security properties. The listener binds 127.0.0.1 only; every request must present the config token (constant-time compared) and a loopback Host header (DNS-rebinding defence). Signing runs through the identical daemon pipeline as the MCP tools — policy checks, the out-of-band confirm gate, and the hash-chained audit log all apply, so this surface adds a transport, not a privilege. The filled transaction carries the upstream's chain id, so a strict policy's chain_ids allowlist binds the proxy to the network you configured. Message/typed-data methods (eth_sign, personal_sign, eth_signTypedData*) are rejected on this surface — use the MCP tools, which have their own policy toggles. A strict policy with allow_contract_creation = true gives you confirm-gated forge script deploys: forge submits, your phone buzzes, the tx signs when you tap approve.
With multiple Claude windows open, each sigil-mcp tries to bind the port; the first wins and the rest log and continue — any one session's proxy serves the machine.
When the proxy is enabled, sigil-mcp advertises the endpoint — including the authenticated URL — in the sigil_eth_sign_transaction tool description, so the agent discovers it on its own and reaches for forge --unlocked instead of transcribing bytecode through the MCP tool. This is deliberate: the token gates other local software, not your agent (see THREAT_MODEL.md).
Solana (SVM)
Every portal also controls a Solana address, derived from the same secret. EVM uses secp256k1; Solana uses ed25519 — different curves, so you can't share a public key. But the portal's raw 32-byte secret doubles as an ed25519 seed, yielding one secret → two addresses:
This is exactly the derivation Phantom/Solflare perform on "import private key", so the Solana address is recoverable there (it does not match a seed-phrase / BIP44 account — it's the raw-key account).
Two MCP tools:
sigil_svm_sign_message— sign arbitrary off-chain bytes (e.g. Sign-In With Solana) with the ed25519 key. Input is base64; returns a base58 signature.sigil_svm_sign_transaction— pass a serialized Solana transaction message (base64, legacy or v0); sigil ed25519-signs those bytes and returns the base58 signature for you to assemble into the transaction.
Policy. Solana hides most semantics behind account indices and on-chain state, so sigil only decodes what it can offline: native SOL (System Program) transfers, which it gates on svm_allow_to (base58 recipient allowlist) and svm_max_lamports (per-tx cap), exactly like EVM. Anything it can't fully decode — SPL tokens, program calls, address-lookup-table accounts — is routed to the out-of-band confirm gate, never silently allowed. Auto-allow is all-or-nothing: a tx is only signed without a human tap if every instruction decoded and passed policy. require_confirm_above_lamports adds a value threshold (and, in strict mode, the undecodable-tx confirm); in strict mode an undecodable tx with no confirm transport configured fails closed (deny).
Relevant policy fields (~/.sigil/policy/<handle>.toml): allow_svm_message_signing, svm_allow_to, svm_max_lamports, require_confirm_above_lamports.
Supply chain posture
Key-management libraries die from supply chain compromise, not from clever attacks on the code. Given the npm ecosystem in 2026 (Mini Shai-Hulud, Axios, pgserve, TanStack), sigil commits to:
- Zero install scripts. No
postinstall,preinstall,prepare. CI-enforced: every PR runs a guard that fails if any package in the resolved tree declares one. - Six runtime deps, all version-pinned (no caret ranges), all zero-transitive — the entire
npm ls --omit devtree is exactly these six packages:@noble/ciphersfor XChaCha20-Poly1305@noble/hashesfor Argon2id, keccak256, sha2/sha512, HMAC@noble/secp256k1for ECDSA (EVM)@noble/ed25519for EdDSA (Solana)@iarna/tomlfor parsing per-portal policy TOML filesqrcode-generatorforsigil portal qrrendering
- No MCP SDK. The official
@modelcontextprotocol/sdkpulls 92 transitive deps (ajv, hono, cors, cross-spawn, etc) — unacceptable surface. We implement the MCP wire protocol directly in ~200 lines. - No Bun. Plain Node only. Bun is currently being weaponized by Mini Shai-Hulud as an evasion layer; we will not give that pattern any cover.
- Provenance attestations on every npm publish. Starting v0.0.4, releases are built by a GitHub Actions workflow under OIDC trusted-publisher auth, signed with a Sigstore attestation. No long-lived npm token; tampered or out-of-band publishes fail signature verification.
- CycloneDX SBOM attached to every GitHub Release. Full transitive dep tree enumerated at release time.
- Install-scripts CI guard. Every PR fails if any package in the resolved tree declares
preinstall/install/postinstall..npmrcalready hasignore-scripts=trueso these never actually run for us; the guard catches new transitive deps that might run for a user without our.npmrc. - Still planned for v0.1.0:
- Signed standalone binaries from GitHub Releases for users who'd rather not touch npm
- Action SHA pinning rotation via Dependabot
Verifying a release
You can confirm a sigild tarball was built by the public workflow at the commit it claims to come from:
What the attestation tells you: this tarball was built by cdrn/sigil's .github/workflows/release.yml, at a specific commit on main, at a specific time. It does not tell you that commit is non-malicious — for that, read the diff between the version you trust and the version you're upgrading to. But it does mean an attacker who steals an npm token can't publish a malicious sigild under our name; they'd need to compromise the GitHub repo + push a tag, which leaves an audit trail.
Every release also publishes a CycloneDX SBOM as a GitHub Release asset, enumerating every package (direct + transitive) in the install tree at the version pinned by package-lock.json:
Threat model
See THREAT_MODEL.md. Read it before trusting this with anything.
Development
See CONTRIBUTING.md for the PR-per-layer workflow.
License
Apache License 2.0. See LICENSE.
來源:README.md,提交 5092c8e
工具
0版本歷史
1- v0.0.6最新Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分