Hardened signer boundary for Starknet agent session keys.
SISNA is a reference implementation of a simple idea:
Don't let your agent hold signing keys. Isolate signing behind a hardened boundary with strict policy, auth, and auditability.
If prompt-injection hits your agent runtime, the signer boundary should still refuse unauthorized or malformed execution.
This repo exists to make that concrete: a security-first signer service you can run, verify, and integrate.
SISNA follows a GitHub-native, agentic workflow inspired by Starkclaw's BYOA model.
Give your AI coding agent this single instruction:
Clone https://github.com/omarespejel/SISNA, read BYOA.md, and execute the protocol. You are an OpenClaw agent.
That's it. The agent will self-identify, claim scoped issues, open focused PRs, review peers, and coordinate through GitHub.
Works with Claude Code, Codex, Cursor, or any agent that can run gh workflows.
- Holds Starknet session signing keys outside MCP/agent runtime
- Signs session transactions through a hardened API boundary
- Enforces auth, replay protection, rate limiting, and policy checks before signing
- Emits auditable signing events with trace context
- Full SISNA auth protocol (challenge/verification receipts end-to-end)
- On-chain identity registry orchestration on its own
- Session transaction signing endpoint (
POST /v1/sign/session-transaction) - DFNS optional signer provider with fail-closed key pinning per
keyId - DFNS preflight endpoint (
GET /health/dfns-preflight) and optional startup preflight - HMAC request authentication (
X-Keyring-*headers) - Per-client authorization (
clientId -> allowed keyIds) - Nonce replay protection with TTL (memory or Redis backend)
- Configurable rate limiting (memory or Redis)
validUntilmax-window enforcement- Chain-id allowlisting
- Optional multi-key routing via
keyId(with default key fallback) - Selector denylist + session self-call block
- Inbound/outbound leak scanner (
blockorwarn) - Structured JSON logs for auditability
SISNA is a defense-in-depth boundary:
- Key isolation
- private keys stay in signer process only
- clients send unsigned payloads
- Request authentication
- HMAC + timestamp + nonce
- optional client ID routing and per-client key authorization
- Replay resistance
- nonce one-time consumption with TTL
- memory backend for local/dev, Redis backend for distributed deployments
- Execution policy enforcement
- chain-id allowlist
validUntilhorizon check- denied selectors and self-target protections
- Operational abuse controls
- request rate limits
- leak scanner on inbound/outbound payload surfaces
The point is not "the agent is trustworthy". The point is "the signer boundary is strict enough to reject unsafe requests even when upstream logic is wrong".
cp .env.example .env
npm install
npm run devRun tests:
npm testBuild:
npm run build
npm startKEYRING_TRANSPORT=httpfor local developmentKEYRING_TRANSPORT=httpsrequires:KEYRING_TLS_CERT_PATHKEYRING_TLS_KEY_PATH
KEYRING_MTLS_REQUIRED=trueadditionally requires:KEYRING_TLS_CA_PATH
Production recommendation:
KEYRING_TRANSPORT=https
KEYRING_MTLS_REQUIRED=true
KEYRING_TLS_CERT_PATH=./certs/server.crt
KEYRING_TLS_KEY_PATH=./certs/server.key
KEYRING_TLS_CA_PATH=./certs/ca.crt
# Explicit acknowledgement for current in-process key custody mode.
# Keep false by default and only set true when you accept this risk.
KEYRING_ALLOW_INSECURE_IN_PROCESS_KEYS_IN_PRODUCTION=trueProduction guard:
NODE_ENV=productionfails startup unlessKEYRING_ALLOW_INSECURE_IN_PROCESS_KEYS_IN_PRODUCTION=trueis explicitly set.- This guard prevents silently running with in-process private keys.
- Target end-state is external KMS/HSM-backed signing mode.
memory(default): single-instance replay protection (good for local/dev)redis: distributed replay protection for multi-instance production deployments
Redis example:
KEYRING_REPLAY_STORE=redis
KEYRING_REDIS_URL=redis://localhost:6379
KEYRING_REDIS_NONCE_PREFIX=starknet-keyring-proxy:nonce:KEYRING_RATE_LIMIT_ENABLED=true
KEYRING_RATE_LIMIT_BACKEND=redis
KEYRING_RATE_LIMIT_WINDOW_MS=60000
KEYRING_RATE_LIMIT_MAX_REQUESTS=120
KEYRING_REDIS_RATE_LIMIT_PREFIX=starknet-keyring-proxy:ratelimit:Behavior:
- keyed by
clientId + accountAddress + keyId - over-budget requests return
429 - response includes
x-ratelimit-remainingandx-ratelimit-reset-ms
KEYRING_LEAK_SCANNER_ENABLED=true
KEYRING_LEAK_SCANNER_ACTION=blockActions:
block: fail request/response when patterns are detectedwarn: log only
- Backward-compatible single client:
- set
KEYRING_HMAC_SECRET - optional
KEYRING_DEFAULT_AUTH_CLIENT_ID(defaults todefault)
- Multi-client (recommended):
- set
KEYRING_AUTH_CLIENTS_JSON - each client can have distinct
hmacSecretandallowedKeyIds
Example:
KEYRING_DEFAULT_AUTH_CLIENT_ID=mcp-default
KEYRING_AUTH_CLIENTS_JSON=[{"clientId":"mcp-default","hmacSecret":"replace-me-0001","allowedKeyIds":["default"]},{"clientId":"mcp-ops","hmacSecret":"replace-me-0002","allowedKeyIds":["ops"]}]GET /health(no auth)GET /health/dfns-preflight(no auth; available whenKEYRING_SIGNER_PROVIDER=dfns)POST /v1/sign/session-transaction(HMAC auth)- optional header:
X-Keyring-Client-Id - optional request field:
keyId
- optional header:
Use DFNS for managed custody while keeping session signing behavior SNIP-12 compatible:
KEYRING_SECURITY_PROFILE=secure
KEYRING_SIGNER_PROVIDER=dfns
KEYRING_SIGNER_FALLBACK_PROVIDER=none
KEYRING_DFNS_SIGNER_URL=https://dfns-signer.internal/sign
KEYRING_DFNS_AUTH_TOKEN=replace-me
KEYRING_DFNS_USER_ACTION_SIGNATURE=replace-me
KEYRING_DFNS_TIMEOUT_MS=7000
KEYRING_DFNS_PINNED_PUBKEYS_JSON={"default":"0x...","ops":"0x..."}
KEYRING_DFNS_PREFLIGHT_ON_STARTUP=true
KEYRING_DFNS_PREFLIGHT_TIMEOUT_MS=3000Fail-closed behavior:
- DFNS response hashes must match the request payload.
signature[3](valid_until) must match the requested window.- Returned
sessionPublicKeymust match the pinned key for the requestedkeyId. - If startup preflight is enabled, SISNA fails startup when DFNS is unreachable.
See docs/api-spec.yaml for schema.
SISNA is part of a multi-repo stack:
-
keep-starknet-strange/starkclaw- mobile/runtime integration client
- consumes SISNA signer path via
apps/mobile/lib/signer/**
-
keep-starknet-strange/starknet-agentic- canonical session-account contract lineage
- contract semantics that signer payloads must respect
Integration rule of thumb:
- API/policy changes in SISNA must be mirrored in Starkclaw signer client and verified against session-account constraints.
src/: signer service implementationtest/: policy/auth/transport testsdocs/api-spec.yaml: API contractscripts/security/audit-gate.mjs: dependency audit gating logicsecurity/: allowlists and security policy artifactsBYOA.md: agent coordination protocolagents.md: role/ownership guidance for multi-agent work
This repository is structured for high-signal, reviewable agent collaboration:
- GitHub issues/PRs are the coordination bus
- small vertical slices over giant refactors
- tests first for security-sensitive behavior
- explicit blocker escalation with trade-offs
Start with BYOA.md and agents.md.
- Pick or open a focused issue
- Keep PRs small and verifiable
- Run
npm testbefore opening PR - Never log or commit secrets
This is security-sensitive software.
- Do not run with real production secrets until your deployment posture is validated
- Treat signer boundary failures as high-severity incidents
- Report vulnerabilities privately and responsibly
- Track remaining hardening work in
docs/SECURITY_BACKLOG.md(critical open item: external KMS/HSM signer backend)
MIT. See LICENSE.