Skip to content

Commit 958ec91

Browse files
authored
Merge pull request #33 from YASSERRMD/fix/crypto-issue
fix: make x402 recipient configurable and clarify Ed25519 key purpose…
2 parents 3e5112b + aadcd64 commit 958ec91

4 files changed

Lines changed: 34 additions & 5 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@
2525

2626
---
2727

28+
## What is siqlah?
29+
30+
siqlah is a **Go + Rust server and CLI toolkit** that creates tamper-evident, cryptographically signed audit receipts for AI API usage. It is a **software transparency and accountability tool** — not a cryptocurrency project, blockchain application, or financial product.
31+
32+
It uses standard **Ed25519 digital signatures** (the same algorithm used by SSH, TLS, and Git) to sign usage receipts, and builds an **append-only Merkle log** (the same data structure used by Certificate Transparency) so that any party can independently verify token counts without trusting the provider or the operator.
33+
34+
The word "private key" appears throughout the codebase and documentation in its standard cryptographic sense: an Ed25519 signing key for receipts and checkpoints, identical in nature to an SSH host key or a TLS client certificate key. It has no connection to cryptocurrency wallets, seed phrases, or blockchain accounts.
35+
36+
---
37+
2838
## The Problem
2939

3040
Every major AI provider — OpenAI, Anthropic, Google — prints a token count in their API response. That number directly determines your invoice. Yet **no independent party verifies it**. You trust a JSON field.
@@ -211,6 +221,8 @@ siqlah supports two signing backends, selectable with `--signing-backend`:
211221
| Ed25519 (default) | `ed25519` | Operator-held private key; fast, simple |
212222
| Fulcio keyless | `fulcio` | OIDC-based ephemeral certificate; no long-lived key required |
213223

224+
> **Note on terminology:** The "private key" used here is an **Ed25519 digital signature key** for signing usage receipts and Merkle checkpoints — a standard cryptographic primitive equivalent to an SSH or TLS signing key. It has no relation to cryptocurrency wallet keys, seed phrases, or blockchain accounts.
225+
214226
Fulcio-signed receipts include a `certificate_pem` field and optionally a `rekor_log_index` for the Rekor transparency log entry.
215227

216228
```bash

cmd/siqlah/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func main() {
5858
rekorAnchor := flag.Bool("rekor-anchor", false, "enable periodic Rekor public anchoring")
5959
rekorInterval := flag.Duration("rekor-anchor-interval", 24*time.Hour, "interval between Rekor anchoring attempts")
6060
inferenceRegion := flag.String("inference-region", "", "cloud region where inference runs (e.g. us-east-1) for carbon reporting")
61+
x402Recipient := flag.String("x402-recipient", "", "EVM address to receive x402 payments (empty disables payment routing)")
6162
flag.Parse()
6263

6364
_, _ = *oidcClientID, *oidcIssuer // surfaced for future integration
@@ -135,6 +136,9 @@ func main() {
135136
}
136137
reg := provider.NewRegistry()
137138
srv := api.NewWithOptions(st, cpBuilder, operatorPub, operatorPriv, reg, version, *inferenceRegion)
139+
if *x402Recipient != "" {
140+
srv.WithX402Recipient(*x402Recipient)
141+
}
138142

139143
// Start periodic batcher.
140144
go func() {

internal/api/server.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type Server struct {
2525
version string
2626
logOrigin string
2727
x402Bridge *x402.Bridge
28+
x402Recipient string
2829
modelReg *model.Registry
2930
energyEst *energy.BenchmarkEstimator
3031
carbonLookup *energy.StaticCarbonLookup
@@ -99,6 +100,13 @@ func NewWithOrigin(
99100
}
100101
}
101102

103+
// WithX402Recipient sets the EVM address that receives x402 payments.
104+
// Must be called before serving requests.
105+
func (s *Server) WithX402Recipient(addr string) *Server {
106+
s.x402Recipient = addr
107+
return s
108+
}
109+
102110
// Routes returns a ServeMux with all API routes registered.
103111
func (s *Server) Routes() *http.ServeMux {
104112
mux := http.NewServeMux()

internal/api/x402_handlers.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func (s *Server) handleIngestWithPayment(w http.ResponseWriter, r *http.Request)
1414
if err != nil {
1515
w.Header().Set("Content-Type", "application/json")
1616
w.WriteHeader(http.StatusPaymentRequired)
17-
pr := x402.NewPaymentRequired(r.Header.Get("X-Request-Id"), defaultSchemes())
17+
pr := x402.NewPaymentRequired(r.Header.Get("X-Request-Id"), s.paymentSchemes())
1818
writeJSON(w, http.StatusPaymentRequired, pr)
1919
return
2020
}
@@ -64,16 +64,21 @@ func (s *Server) handleGetReceiptPayment(w http.ResponseWriter, r *http.Request)
6464
writeJSON(w, http.StatusOK, resp)
6565
}
6666

67-
// defaultSchemes returns the x402 payment schemes accepted by this operator.
68-
// In production these would be loaded from configuration.
69-
func defaultSchemes() []x402.PaymentScheme {
67+
// paymentSchemes returns the x402 payment schemes accepted by this operator.
68+
// Recipient is set via --x402-recipient; the zero address is returned when unconfigured
69+
// so that operators must explicitly opt-in before real payments can be routed.
70+
func (s *Server) paymentSchemes() []x402.PaymentScheme {
71+
recipient := s.x402Recipient
72+
if recipient == "" {
73+
recipient = "0x0000000000000000000000000000000000000000"
74+
}
7075
return []x402.PaymentScheme{
7176
{
7277
Scheme: "x402/evm-token",
7378
Network: "base-mainnet",
7479
Amount: "1000000", // 1 USDC (6 decimals)
7580
Token: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // USDC on Base
76-
Recipient: "0x0000000000000000000000000000000000000000", // operator wallet
81+
Recipient: recipient,
7782
},
7883
}
7984
}

0 commit comments

Comments
 (0)