@quaestor/core
v0.1.0
Published
Local daemon issuing Ed25519 HD-key signed payment mandates with a BLAKE3-chained ledger.
Downloads
136
Readme
quaestor-core
Demo
Phase 2.0 — Local policy LLM (latest). Intent enforcement, locally. Watch · Phase 1.5a
Local-first agent payments. Quaestor lets an autonomous agent pay merchants from your machine without ever uploading the keys — the daemon issues short-lived, amount-capped, recipient-bound mandate JWTs from a vault stored on the device, and every issuance and redemption lands on a tamper-evident ledger you can audit. This repo is the daemon: the vault, the signer, the ledger.
How it fits together
flowchart LR
Agent["Agent / App"] -->|"mandate JWT"| Bridge["quaestor-bridge<br/>(protocol translator)"]
Bridge -->|"POST /mandate/redeem<br/>X-Local-Auth"| Core["quaestor-core<br/>127.0.0.1:3402"]
Core -->|"x402-shape Credential"| Bridge
Bridge -->|"X-PAYMENT / Authorization: Payment /<br/>X-A2A-Payment"| Merchant["Merchant /<br/>Facilitator"]
Bridge -->|"POST /ledger/receipt"| Core
Core --> Vault[("Encrypted vault<br/>HD Ed25519, AES-256-GCM<br/>KEK in OS keychain")]
Core --> Ledger[("BLAKE3-chained<br/>SQLite WAL ledger")]What's working today
- ✅ HTTP daemon at
127.0.0.1:3402— every endpoint gated byX-Local-Auth, non-loopback peers refused with403 - ✅ HD Ed25519 vault: BIP-39 → SLIP-0010, AES-256-GCM at rest, KEK in OS keychain (
keytar, servicequaestor-core) - ✅ EdDSA mandate JWTs with full claim set (
amount_max,use_counter_max,exp,nbf,purpose_tag,jti, …) - ✅ Redemption returns x402-shape
Credential(cred_<jti>_<use_index>) with use-counter ratcheting - ✅ BLAKE3-chained SQLite WAL ledger — tamper-evident via
quaestor ledger verify - ✅
POST /ledger/receiptcloses the mandate → pay → receipt loop, idempotent oncredential_id - ✅ Resumable replay via
GET /ledger/since/:hash - ✅ 24/24 tests pass,
tsc --noEmitclean
Quickstart
Install from npm:
pnpm add -g @quaestor/corepnpm install && pnpm build
node ./bin/run.js init # captures mnemonic + writes ~/.config/quaestor/auth.token
node ./bin/run.js start # listens on 127.0.0.1:3402Use Node 22 LTS — nvm use picks it up from .nvmrc. Native bindings (better-sqlite3, keytar) break on Node 23+.
Trust boundaries
These are load-bearing. Violating any one of them is a regression, not a feature.
- Private keys never leave the host. The HTTP API returns only signed JWTs and derived credentials. Raw key material is never serialized.
- Loopback-only. Bound to
127.0.0.1; non-loopback peers get403. Every endpoint additionally requiresX-Local-Auth, compared withcrypto.timingSafeEqual. - Tamper-evident ledger. Every state change is a row with
prev_hash+entry_hash = BLAKE3(ts | type | jti | payload | prev_hash).quaestor ledger verifywalks from genesis and reports the first break. - Mandate validity is decided in core. Signature, expiry,
nbf,amount_max, anduse_counter_maxare checked on every redeem. The bridge never sees a private key and never makes the call. - No retroactive mutation. No endpoint deletes or rewrites a row. Disputes are resolved by appending a new entry kind.
- Idempotency on
credential_id. A second receipt for the same credential returns409 duplicate_receipt. - Trust nothing at startup. A vault that won't decrypt with the keychain KEK or a ledger whose chain fails
verify()causes the daemon to refuse to serve.
Demo
90-second screen recording — one mandate, three protocols, one ledger:
→ demo.mp4 (in quaestor-bridge)
HTTP API (cheat sheet)
All endpoints require X-Local-Auth: <token> and a loopback peer.
POST /mandate/request— issue a signed JWT or a 402 challengePOST /mandate/redeem— verify + decrement use-counter, return CredentialPOST /ledger/receipt— record an upstream confirmation; idempotent oncredential_idGET /ledger/since/:hash— resumable ledger replay
Storage paths: $XDG_DATA_HOME/quaestor/{vault,ledger}.db, auth token at $XDG_CONFIG_HOME/quaestor/auth.token, master KEK in OS keychain (service quaestor-core).
More
- Status, verified capabilities, known gaps: STATUS.md
- Companion repo: quaestor-bridge — protocol translator (MPP, x402, AP2)
