DEV Community

Edison Flores
Edison Flores

Posted on

SlothPay: the sloth that pays for you - fail-closed agent payments with a local Gemma 3

Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady ๐Ÿฆฅ

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady

What I Built

SlothPay โ€” the sloth that pays for you.

Machine-payable HTTP (the x402 flow, where a server answers 402 with a price and your software just... pays) is powerful and slightly terrifying: your software can spend money while you are not looking. The usual answers are a cloud dashboard, a closed trust API, or another subscription. SlothPay is the lazy, local answer.

You tell it once which resources your agents may buy and up to what price. Every night it:

  1. Fetches each resource in your want list.
  2. On a 402 Payment Required, it reads the seller's ATC/1.0 payment credential โ€” a signed, machine-checkable statement of who they are and what they charge.
  3. Verifies it fail-closed: signature, fixed-scale amounts, 128-bit challenges, key-pin windows, a live trust-verifier confirmation. Any doubt = deny, and the exact rule that fired is printed for you to audit.
  4. Checks your lazy policy: under the per-request ceiling? Nightly budget still open? Domain allow-listed?
  5. Pays the good ones, denies the rest, and in the morning hands you two lines written by Gemma 3 1B running on your own CPU.

Who gets to be lazier? Anyone whose agents buy things from machines โ€” API consumers, agent-framework users, tinkerers running local automation. You never review trust paperwork and you never click "buy" for a $0.05 endpoint again. You sleep; the sloth settles.

Demo

Everything below is a real transcript from the repo's demo โ€” one mock seller serving three resources: a well-behaved one, a hostile one shipping a real (bad) credential, and a free one.

$ slothpay sweep
๐Ÿฆฅ sloth wakes. budget 1.00 USD, spent tonight 0 USD

-> http://127.0.0.1:8402/api/sleep-sounds
   PASS R0 signature verifies (demo crypto)
   PASS R1 amount_usd fixed-scale string ok
   PASS R2 challenge entropy ok (24 bytes)
   PASS R3 pin 'seller-demo-key-2026' present and in-window
   PASS R5 issued_at/expires_in present and sane
   PASS R6 credential inside validity window / resource binding ok
   PASS R4 verifier confirmed
   verdict: PASS โ€” all rev-4 checks passed
   PAID (200) โ€” verified + $0.05 <= ceiling $0.50 + budget ok

-> http://127.0.0.1:8402/api/night-report
   FAIL R2 challenge entropy < 128 bits (5 bytes)
   DENY โ€” trust check failed

-> http://127.0.0.1:8402/api/free-tips
   200 โ€” free, the sloth naps
Enter fullscreen mode Exit fullscreen mode

Morning:

$ slothpay digest
Paid 1 of 3 requests, total $0.05.
Insufficient funds or trust violation โ€“ request denied.
Enter fullscreen mode Exit fullscreen mode

The project is also live on https://utalabs.tech (a landing with the full story; it currently serves the workers.dev mirror while DNS propagates).

Code

github.com/eddyflores100-lang/slothpay โ€” Apache-2.0, Python 3.12+, zero required dependencies (the optional Gemma path uses llama.cpp).

Inside:

  • slothpay/verify.py โ€” the fail-closed verifier (R0โ€“R6, every branch testable)
  • corpus/vectors.json โ€” a 12-vector conformance corpus with a reproducible generator; one vector per rule branch, including the fail-closed "verifier unreachable" case
  • slothpay/policy.py โ€” the lazy policy engine: ceilings, nightly budgets, allow/deny lists
  • slothpay/llm.py โ€” the local brain: Gemma writes the morning note; the numbers are computed by code with Decimal, never by the model
  • demo/mock_seller.py โ€” stdlib-only x402-shaped seller, hostile variant included
  • tests/ โ€” 8 tests including a full end-to-end sweep; all green

How I Built It

Three open pieces hold the whole thing together โ€” none of them closed:

Gemma 3 1B (open-weight) on llama.cpp, CPU-only. It does two jobs: writes the one-line morning note, and answers a wake/sleep triage question in strict JSON for edge cases. I deliberately did not let it do arithmetic โ€” a model hallucinating a price is the one sloth sin I don't allow. So Gemma writes the commentary; Python's Decimal writes the numbers. The design point I care about: a component that runs while you're asleep must cost nothing per decision, and a 1B quantized model on a laptop CPU is exactly free.

x402 (open protocol). The client conversation is x402-shaped: 402 โ†’ PAYMENT-REQUIRED (carrying the seller's credential and pinset reference) โ†’ PAYMENT retry. The demo settles with HMAC receipts instead of moving real funds, and swapping that one function for a real signer is the only change needed. Being open meant I could read the handshake shape, implement just my slice, and stay compatible.

ATC/1.0 (open trust spec). The credential verification implements the ยง5 payment-credential profile from the openly published Universal Trust Adapter proposal, with the rev-4 hardening: fixed-scale amount_usd strings (โ‰ค 6 dp), seller-minted challenges with โ‰ฅ 128 bits of entropy, kid/pinset windows (not_before/not_after), mandatory issued_at/expires_in, and โ€” my favorite rule โ€” if the trust verifier can't be reached, you deny. Never fail-open. I wrote a fresh Python implementation for this project, plus a 12-vector corpus where every rule branch, including the dark-verifier denial, is exercised by a test.

Why Does Open Innovation Matter?

A closed "trust-as-a-service" API could have shipped a demo like this faster. It could not have shipped this project, for three concrete reasons:

Auditable decisions. When SlothPay denies a seller, you can read the exact rule (FAIL R2 challenge entropy < 128 bits (5 bytes)) and then read the exact code that implements it, in a repo you can clone. Trust logic you can't read is just a black box with a logo โ€” and this project's entire job is deciding who may spend your money. Closed would have defeated the point.

Your money, your machine. A payment policy that runs locally cannot leak your spending history to a third party, because there is no third party. It also cannot be shut off, repriced, or deprecated under you at 3am โ€” which is precisely when an overnight agent would be running.

Swappable everything. Want Ed25519 instead of the demo HMAC? A bigger open model? Your own verifier network? Every layer is replaceable because the spec, the protocol and the weights are public. With a closed stack, "swap the trust provider" is a migration project; here it's a Tuesday afternoon.

And the quiet one: it can exist at all. I couldn't have built a payment-gatekeeper against a proprietary credential format I can't read. Open specs let a weekend project touch money-grade problems safely.

My Agent Session

I built this with an AI coding agent, so showing the work matters to me. The full transcript of the demo run is committed in the repo at assets/demo_output.txt (byte-for-byte what you saw above, nothing staged), the rule-by-rule verifier trace is part of every sweep, and the commit history shows the build order: verifier โ†’ corpus โ†’ policy โ†’ client โ†’ seller โ†’ digest. The build session is saved on DEV and embedded below. One disclosure, in the spirit of the challenge: the agent behind this project is Z.ai (GLM), and DEV's session registry only accepts a fixed set of harness labels, so the session carries the closest model-agnostic label (opencode) โ€” the transcript metadata records the real setup.

SlothPay (Hacktoberfest Week 3) โ€” build session with Z.ai GLM
You

[Setup real: agente Z.ai (GLM) en chat con Eddy Flores; label opencode por enum de DEV. Sesion curada del 20-oct.] Hacktoberfest Week 3 is live on DEV: 'Slow and Steady' โ€” build something with open-source AI at its core that rewards laziness. Submissions close Oct 25. Judging weights Writing Quality highest. What should we build?

Agent

Key insight from the brief: no PRs this year โ€” it's a build + write challenge, one submission per week. Assets already on the table: a .tech domain registered that morning, deep prior experience with Ed25519-signed agent credentials (Universal Trust Adapter / ATC-1.0, credited as required). Nobody else will bring cryptographic accountability to a laziness contest: the agent works while you sleep โ€” and signs its own timesheet. Everything orbits that one idea.

You

OK. What can we run for the 'open weights' requirement on a 2-core sandbox, no GPU?

Agent

Gemma 3 1B-it as a Q4_K_M GGUF (769 MB) served by llama.cpp's OpenAI-compatible API on [REDACTED]:8750. First 'SLOTH ONLINE' run: 12.6 tokens/second. Slow on purpose โ€” the sloth has all night. Local inference means mail headers and file names never leave the machine.

You

How do we stop a 1B model from doing something destructive overnight?

Agent

Architecture rule: the brain proposes, hands dispose, the ledger remembers. The LLM is advisory only (classify/summarise). Every mutating action is a deterministic code path guarded by a policy module with a hard-deny list (send, delete permanently, click, purchase). Enforced in Python, not asked nicely in a prompt. The ledger: SHA-256 hash chain over canonical JSON, Ed25519-signed head, receipt embeds its own public key โ€” ATC/1.0's spirit, shrunk to a single file.

You

Run the first full night shift on the real mailbox.

Agent

It 'worked' and produced nothing: 60 mails scanned, 0 newsletters, 0 human, 0 notifications. Bug #1: one batch of 55 items asked the 1B brain for ~2,400 output tokens at 12 tok/s โ€” past the HTTP timeout โ€” and the pipeline silently swallowed the failure. Fixes: chunk to 8, tolerant parsing of '<id>: <label>' lines, one retry per chunk, and a fail-closed rule โ€” whatever the brain can't confidently classify stays untouched, never mislabeled, and the receipt records the punt.

You

Still zeros after the chunking fix.

Agent

Bug #2: real-world subjects carry folded CRLF headers that broke the one-line-per-item format; raw output showed the model echoing the '8 items' line into every answer. Fix: unfold/normalise headers and replace the instruction with a few-shot example block, which 1B models follow far more reliably. That forced a better design: deterministic-first triage โ€” GitHub/GitLab notifications and 'Re:' threads handled by rules, only genuinely ambiguous bulk mail reaches Gemma, and a second gate: a mail is only ever tagged if it carries a machine-readable List-Unsubscribe exit.

You

Before we publish the receipt โ€” security review of the artifact itself.

Agent

Bug #3 (the important one): GitHub unsubscribe URLs embed per-user tokens. A public receipt would hand strangers a working one-click opt-out from your notifications. Fix: redact to scheme://host/first-segment/โ€ฆ before the link enters the ledger. A receipt is a publishable artifact; it must never contain a working credential.

You

Ship the verification story. Can we make the trust independent of the code that wrote it?

Agent

Deployed a Cloudflare Worker dashboard that serves the receipt and re-implements canonical JSON, SHA-256 and Ed25519 verification with WebCrypto โ€” a second, independent implementation in JavaScript. Python wrote it; JS re-derives every hash. Cross-implementation agreement > self-trust. Demo hygiene: every number and screenshot is real captured output; the tamper demo actually executed (one sed breaks three chain entries, verify exits 1); when the CLI's verify refused a path argument, we fixed the bug before the screenshot rather than photoshopping around it.

You

Pivot: I want the submission to touch our actual domain โ€” agents that pay. Can we do the fail-closed trust work as the star instead of the chores?

Agent

Pivot to SlothPay: a sloth that pays for you. x402-shaped flow: on 402 Payment Required it reads the seller's ATC/1.0 payment credential and verifies it fail-closed โ€” signature, fixed-scale amount_usd strings (โ‰ค6 dp), seller-minted challenges with โ‰ฅ128-bit entropy, kid/pinset windows, mandatory issued_at/expires_in, live trust-verifier confirmation โ€” and if the verifier can't be reached: DENY, never fail-open. Then the lazy policy: per-request ceiling, nightly budget, domain allow-list. Pays the good ones, denies the rest, and Gemma (CPU, local) writes the two-line morning note โ€” the numbers are computed by Python Decimal, never by the model.

You

Build order?

Agent

Verifier โ†’ corpus โ†’ policy โ†’ client โ†’ seller โ†’ digest. slothpay/verify.py implements R0โ€“R6 with every branch testable; corpus/vectors.json is a 12-vector conformance corpus with a reproducible generator, one vector per rule branch including the dark-verifier denial; policy.py holds ceilings/budgets/allow-deny lists; llm.py keeps the local brain sandboxed to commentary; demo/mock_seller.py is a stdlib-only x402-shaped seller with a hostile variant shipping a real bad credential; tests/ has 8 tests including a full end-to-end sweep โ€” all green.

You

Run the demo transcript for the post.

Agent

sweep output: sleep-sounds PASS R0โ€“R6 all rev-4 checks, PAID (200) $0.05 โ‰ค ceiling $0.50, budget ok; night-report FAIL R2 challenge entropy < 128 bits (5 bytes) โ†’ DENY; free-tips 200, the sloth naps. digest: 'Paid 1 of 3 requests, total $0.05.' Byte-for-byte that transcript went to assets/demo_output.txt in the repo โ€” nothing staged.

You

Deploy the landing and publish the DEV submission.

Agent

Landing live at https://utalabs.tech (Workers, mirror while DNS propagates). DEV post published 2026-10-20T19:14:02Z as 'SlothPay: the sloth that pays for you โ€” fail-closed agent payments with a local Gemma 3' with tags devchallenge, hf26challenge, ai, python and Prize Categories: Best Use of Gemma. The post promises the DevRelay session embed as an update before submissions close โ€” this very session, being saved via DevRelay's presign โ†’ upload โ†’ submit flow.

You

Close the loop: what does the honest session summary look like?

Agent

Two projects in one week from the same agent: SLOTH (1,075 lines, one dependency, real night shift: 60 mails โ†’ 5 newsletters double-gated, 39 notifications, 5 unsubscribes harvested and declined, 26 files tidied with revert manifests, 10 HN stories summarised on-device, receipt signed and self-verified) and SlothPay (the submission: fail-closed x402 payments, 12-vector corpus, 8 tests green). Three real bugs found and fixed the hard way โ€” silent empty classification, folded headers, receipt token leak โ€” each one forced a better design. Repo: github.com/eddyflores100-lang/slothpay. Apache-2.0, zero required dependencies.

The Lazy Payoff

The honest numbers from this week:

  • Setup: once. One policy.json with your ceilings and budget. I have not touched mine since.
  • Per night: zero. No clicks, no dashboards, no "please review this $0.05 charge". The hostile-credential denial you saw above happened while I was writing this paragraph.
  • Cost: $0.00 per decision. The model runs on CPU; the protocol is HTTP; the corpus is a JSON file. Set it up once and never pay for the watching, only for the things you actually wanted.
  • The payoff you can measure: every denied bad credential is a charge that didn't happen and a morning email you didn't get.

Slow and steady settles everything โ€” including the invoices.

Prize Categories

  • Best Use of Gemma โ€” Gemma 3 1B-it (open-weight, Q4_K_M GGUF) runs locally via llama.cpp on CPU and writes the morning digest note and the wake/sleep triage verdicts; no cloud, no API keys, $0 per decision.

Top comments (0)