跳至主要内容
小龙虾小龙虾AI
🤖

ERC-8128

Sign and verify HTTP requests with Ethereum wallets using ERC-8128. Use when building authenticated APIs that need wallet-based auth, making signed requests...

下载288
星标2
版本1.0.0
开发工具
安全通过
🔗API

技能说明


name: erc8128 description: Sign and verify HTTP requests with Ethereum wallets using ERC-8128. Use when building authenticated APIs that need wallet-based auth, making signed requests to ERC-8128 endpoints, implementing request verification in servers, or working with agent-to-server authentication. Covers both the @slicekit/erc8128 JS library and the erc8128 CLI.

ERC-8128: Ethereum HTTP Signatures

ERC-8128 extends RFC 9421 (HTTP Message Signatures) with Ethereum wallet signing. It enables HTTP authentication using existing Ethereum keys—no new credentials needed.

📚 Full documentation: erc8128.slice.so

When to Use

  • API authentication — Wallets already onchain can authenticate to your backend
  • Agent auth — Bots and agents sign requests with their operational keys
  • Replay protection — Signatures include nonces and expiration
  • Request integrity — Sign URL, method, headers, and body

Packages

PackagePurpose
@slicekit/erc8128JS library for signing and verifying
@slicekit/erc8128-cliCLI for signed requests (erc8128 curl)

Library: @slicekit/erc8128

Sign requests

import { createSignerClient } from '@slicekit/erc8128'
import type { EthHttpSigner } from '@slicekit/erc8128'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount('0x...')

const signer: EthHttpSigner = {
  chainId: 1,
  address: account.address,
  signMessage: async (msg) => account.signMessage({ message: { raw: msg } }),
}

const client = createSignerClient(signer)

// Sign and send
const response = await client.fetch('https://api.example.com/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ amount: '100' }),
})

// Sign only (returns new Request with signature headers)
const signedRequest = await client.signRequest('https://api.example.com/orders')

Verify requests

import { createVerifierClient } from '@slicekit/erc8128'
import type { NonceStore } from '@slicekit/erc8128'
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

// NonceStore interface for replay protection
const nonceStore: NonceStore = {
  consume: async (key: string, ttlSeconds: number): Promise<boolean> => {
    // Return true if nonce was successfully consumed (first use)
    // Return false if nonce was already used (replay attempt)
  }
}

const publicClient = createPublicClient({ chain: mainnet, transport: http() })
const verifier = createVerifierClient(publicClient.verifyMessage, nonceStore)

const result = await verifier.verifyRequest(request)

if (result.ok) {
  console.log(`Authenticated: ${result.address} on chain ${result.chainId}`)
} else {
  console.log(`Failed: ${result.reason}`)
}

Sign options

OptionTypeDefaultDescription
binding"request-bound" | "class-bound""request-bound"What to sign
replay"non-replayable" | "replayable""non-replayable"Include nonce
ttlSecondsnumber60Signature validity
componentsstring[]Additional components to sign
contentDigest"auto" | "recompute" | "require" | "off""auto"Content-Digest handling

request-bound: Signs @authority, @method, @path, @query (if present), and content-digest (if body present). Each request is unique.

class-bound: Signs only the components you explicitly specify. Reusable across similar requests. Requires components array.

📖 See Request Binding for details.

Verify policy

OptionTypeDefaultDescription
maxValiditySecnumber300Max allowed TTL
clockSkewSecnumber0Allowed clock drift
replayablebooleanfalseAllow nonce-less signatures
classBoundPoliciesstring[] | string[][]Accepted class-bound component sets

📖 See Verifying Requests and VerifyPolicy for full options.

CLI: erc8128 curl

For CLI usage, see references/cli.md.

Quick examples:

# GET with keystore
erc8128 curl --keystore ./key.json https://api.example.com/data

# POST with JSON
erc8128 curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"foo":"bar"}' \
  --keyfile ~/.keys/bot.key \
  https://api.example.com/submit

# Dry run (sign only)
erc8128 curl --dry-run -d @body.json --keyfile ~/.keys/bot.key https://api.example.com

📖 See CLI Guide for full documentation.

Common Patterns

Express middleware

import { verifyRequest } from '@slicekit/erc8128'
import type { NonceStore } from '@slicekit/erc8128'
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const publicClient = createPublicClient({ chain: mainnet, transport: http() })

// Implement NonceStore (Redis example)
const nonceStore: NonceStore = {
  consume: async (key, ttlSeconds) => {
    const result = await redis.set(key, '1', 'EX', ttlSeconds, 'NX')
    return result === 'OK'
  }
}

async function erc8128Auth(req, res, next) {
  const result = await verifyRequest(
    toFetchRequest(req), // Convert Express req to Fetch Request
    publicClient.verifyMessage,
    nonceStore
  )

  if (!result.ok) {
    return res.status(401).json({ error: result.reason })
  }

  req.auth = { address: result.address, chainId: result.chainId }
  next()
}

Agent signing (with key file)

import { createSignerClient } from '@slicekit/erc8128'
import type { EthHttpSigner } from '@slicekit/erc8128'
import { privateKeyToAccount } from 'viem/accounts'
import { readFileSync } from 'fs'

const key = readFileSync(process.env.KEYFILE, 'utf8').trim()
const account = privateKeyToAccount(key as `0x${string}`)

const signer: EthHttpSigner = {
  chainId: Number(process.env.CHAIN_ID) || 1,
  address: account.address,
  signMessage: async (msg) => account.signMessage({ message: { raw: msg } }),
}

const client = createSignerClient(signer)

// Use client.fetch() for all authenticated requests

Verify failure reasons

type VerifyFailReason =
  | 'missing_headers'
  | 'label_not_found'
  | 'bad_signature_input'
  | 'bad_signature'
  | 'bad_keyid'
  | 'bad_time'
  | 'not_yet_valid'
  | 'expired'
  | 'validity_too_long'
  | 'nonce_required'
  | 'replayable_not_allowed'
  | 'replayable_invalidation_required'
  | 'replayable_not_before'
  | 'replayable_invalidated'
  | 'class_bound_not_allowed'
  | 'not_request_bound'
  | 'nonce_window_too_long'
  | 'replay'
  | 'digest_mismatch'
  | 'digest_required'
  | 'alg_not_allowed'
  | 'bad_signature_bytes'
  | 'bad_signature_check'

📖 See VerifyFailReason for descriptions.

Key Management

For agents and automated systems:

MethodSecurityUse Case
--keyfileMediumUnencrypted key file, file permissions for protection
--keystoreHighEncrypted JSON keystore, password required
ETH_PRIVATE_KEYLowEnvironment variable, avoid in production
Signing serviceHighDelegate to external service (SIWA, AWAL)

Documentation

如何使用「ERC-8128」?

  1. 打开小龙虾AI(Web 或 iOS App)
  2. 点击上方「立即使用」按钮,或在对话框中输入任务描述
  3. 小龙虾AI 会自动匹配并调用「ERC-8128」技能完成任务
  4. 结果即时呈现,支持继续对话优化

相关技能