Wallet Auth: Gate Any API on What a Wallet Holds

Every authentication system in production today proves who you are. OAuth proves you logged in with Google. API keys prove you registered. JWTs prove a server issued you a session. Wallet Auth proves what you own. That is a different primitive — and it points to something bigger: blockchain state converted into standard internet credentials, consumable by any system that understands a JWT. Wallet Auth does not prove control of a wallet. It proves that a wallet meets specific on-chain conditions.

What Wallet Auth is

Wallet Auth by InsumerAPI is a new category of API access control where access decisions are based on wallet state: what a wallet holds, what it has staked, what credentials it carries. Not identity. Not login sessions. On-chain facts.

The output is a standard ES256 JWT bearer token. The input is a blockchain verification across any of 32 supported chains. Between those two points, InsumerAPI reads the chain, evaluates the conditions you specify, and signs a JWT containing the results. The verifying side never touches a blockchain. That is the architectural point: blockchain data consumed without blockchain infrastructure.

Add "format": "jwt" to any POST /v1/attest request. The response includes a jwt field alongside the standard sig and kid. Same endpoint, same credit cost, one extra field.

How it works

A Wallet Auth request is identical to a standard attestation request with one addition:

Wallet address
      ↓
POST /v1/attest  { format: "jwt" }
      ↓
InsumerAPI reads blockchain state
      ↓
ES256 JWT credential (30 min TTL)
      ↓
API gateway / middleware verifies via JWKS
      ↓
Access decision
curl -X POST https://api.insumermodel.com/v1/attest 
  -H "Content-Type: application/json" 
  -H "X-API-Key: YOUR_API_KEY" 
  -d '{
    "wallet": "0x1234...abcd",
    "format": "jwt",
    "conditions": [{
      "type": "token_balance",
      "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "chainId": 1,
      "threshold": 1000,
      "decimals": 6
    }]
  }'

The response includes data.jwt, a three-part ES256-signed JWT. Decode the payload and you get standard claims:

{
  "iss": "https://api.insumermodel.com",
  "sub": "0x1234...abcd",
  "jti": "ATST-A7C3E1B2D4F56789",
  "iat": 1741089600,
  "exp": 1741091400,
  "pass": true,
  "results": [{
    "condition": 0,
    "label": "USDC >= 1000",
    "type": "token_balance",
    "chainId": 1,
    "met": true,
    "evaluatedCondition": {
      "type": "token_balance",
      "contractAddress": "0xA0b8...eB48",
      "chainId": 1,
      "threshold": 1000,
      "decimals": 6
    },
    "conditionHash": "0x3a7f...",
    "blockNumber": "0x12a3b4f",
    "blockTimestamp": "2026-03-04T12:00:00Z"
  }],
  "conditionHash": ["0x3a7f..."]
}

iss is the issuer (https://api.insumermodel.com). sub is the wallet address. exp is 30 minutes from issuance. pass is the overall boolean. results contains per-condition detail including the block number at which the chain was read. conditionHash is a SHA-256 of the evaluated conditions, so verifiers can confirm exactly what was checked. Short expiration ensures the credential reflects recent chain state.

Why this is different

Traditional auth answers: “Is this user who they claim to be?” Wallet Auth answers: “Does this wallet meet these conditions?” The distinction matters because it enables access control patterns that identity-based auth cannot express.

        - **Gate a premium API tier on stablecoin holdings.** Any wallet holding 10,000+ USDC on any of 7 chains gets access. No signup. No KYC. The JWT proves the holding.
        - **Restrict a governance endpoint to token holders.** Only wallets holding UNI, AAVE, or ARB can call the endpoint. The JWT encodes which conditions were met.
        - **Require a Coinbase Verified Account attestation.** The JWT proves a wallet has an EAS attestation on Base from Coinbase. No raw credential data exposed.
        - **Gate content on NFT ownership.** A BAYC holder gets a JWT that any downstream service can verify. The NFT contract is in the claims. The balance is not.

In each case, the access decision is based on verifiable on-chain state. The JWT is the proof. The verifier needs no blockchain connection.

The portable credential

A Wallet Auth JWT can be forwarded from Service A to Service B and verified without re-querying the chain. Service B fetches the JWKS public key once, checks the ES256 signature, reads the claims, and makes an access decision. No blockchain infrastructure. No Insumer SDK. No network call to InsumerAPI at request time.

The JWKS endpoint is at https://insumermodel.com/.well-known/jwks.json (also available at GET /v1/jwks). The key ID is insumer-attest-v1. Any standard JWT library can verify the token: jose, jsonwebtoken, PyJWT, go-jose, or any JOSE-compliant implementation.

This means a single attestation call produces a credential that can be verified by an unlimited number of downstream services. The JWT is self-contained proof of wallet state at a specific block height, valid for 30 minutes.

Gateway compatible

Wallet Auth JWTs are standard ES256 JWTs backed by a JWKS endpoint. They work with any system that accepts JWKS-backed JWTs:

        - Kong
        - Nginx (ngx_http_auth_jwt module)
        - Cloudflare Access
        - AWS API Gateway
        - Azure API Management
        - Traefik
        - Envoy
        - Any OAuth 2.0 middleware

Point your gateway’s JWKS URL at https://insumermodel.com/.well-known/jwks.json. Configure it to require alg: ES256 and validate iss: https://api.insumermodel.com. That is the entire blockchain integration.

Verification with insumer-verify

The insumer-verify npm package (v1.3.0) auto-detects the input format. Pass either the full response object or the JWT string directly:

import { verifyAttestation } from "insumer-verify";

// response = await res.json() — the full API envelope
// Pass the JWT string directly from response.data.jwt
const result = await verifyAttestation(response.data.jwt, {
  jwksUrl: "https://insumermodel.com/.well-known/jwks.json"
});

console.log(result.valid);   // true
console.log(result.payload); // the verified attestation data

The package runs signature verification, expiry checks, and condition hash validation. Zero dependencies. Works in Node.js 18+ and browsers.

What it costs

Nothing extra. A Wallet Auth attestation costs the same 1 credit ($0.04) as a standard attestation. The format: "jwt" parameter adds the JWT to the response alongside the existing sig field. Both are always available. Merkle proof attestations with JWT cost 2 credits ($0.08), same as without.

Where to start

Get a free API key at insumermodel.com/developers. Add "format": "jwt" to any attestation request. Read the full Wallet Auth documentation for code examples in JavaScript, Python, and curl.

Wallet Auth is available in all four InsumerAPI SDKs: MCP server (v1.7.5), LangChain (v0.9.13), ElizaOS (v1.0.4), and the OpenAI GPT.

A note on error handling: If the API cannot reach one or more upstream data sources after retries, the attestation request returns HTTP 503 with error.code: "rpc_failure". No JWT is issued, no signature is returned, and no credits are charged. This is a retryable error — wait 2–5 seconds and retry. Do not treat rpc_failure as pass: false; it means the API refused to sign an unverified result.

    Share this article

        [

            Share on X
        ](#)
        [

            LinkedIn
        ](#)
        [

            Reddit
        ](#)


            Discord



            Copy Link

InsumerAPI is free to start. Get an API key and try it. View API Docs

Local LLM Unleashed: Faster Inference, Instant Starts, & Open TTS

Local LLM Unleashed: Faster Inference, Instant Starts, & Open TTS

Today’s Highlights

This week, we’re diving into breakthroughs that will redefine your local LLM experience, from dramatically faster inference and sub-second cold starts to a new SOTA open-weight text-to-speech model. Prepare to optimize your RTX GPUs and self-hosted infrastructure with tools and techniques you can implement today.

Mistral AI Releases Voxtral TTS: Open-Weight, SOTA Text-to-Speech (r/LocalLLaMA)

Source: https://reddit.com/r/LocalLLaMA/comments/1s46ylj/mistral_ai_to_release_voxtral_tts_a/

Mistral AI has released Voxtral TTS, a 3-billion-parameter text-to-speech model with open weights that is already making waves in the local LLM community. This model claims to outperform ElevenLabs Flash v2.5 in human preference tests, setting a new bar for high-quality, accessible TTS. Crucially for our readers, Voxtral is designed to run efficiently on consumer hardware, requiring approximately 3 GB of RAM, making it highly suitable for RTX GPUs and self-hosted inference setups. It boasts an impressive 90-millisecond time-to-first-audio (TTFA), ensuring near real-time responsiveness for interactive applications.

Supporting nine languages, Voxtral opens up significant opportunities for developers to integrate advanced, multilingual voice capabilities into their projects without relying on expensive cloud APIs. The availability of open weights means direct access for fine-tuning, experimentation, and local deployment, fostering innovation in areas like conversational AI, voice assistants, and accessibility tools. This release provides a powerful, free alternative for developers who prioritize data privacy, low latency, and full control over their AI stack.

Comment: Finally, a truly competitive open-source TTS model that runs locally on my RTX 4090 with low VRAM. This is a game-changer for building fully self-contained conversational agents, leaving ElevenLabs in the dust for anything where privacy and local control are paramount. I can’t wait to pipe this directly into my agent’s voice output.

RotorQuant: Boosting Inference Speed 10-19x with Clifford Algebra Quantization (r/LocalLLaMA)

Source: https://reddit.com/r/LocalLLaMA/comments/1s44p77/rotorquant_1019x_faster_alternative_to_turboquant/

A groundbreaking new quantization method called RotorQuant has emerged, promising a 10-19x speedup over the existing TurboQuant technique while using 44x fewer parameters. This innovative approach reimagines vector quantization using Clifford Algebra, a powerful mathematical framework, and has been implemented on both CUDA and Metal Shaders. For hands-on developers pushing the limits of local LLM inference on RTX GPUs, this represents a significant leap in efficiency. Faster inference directly translates to higher throughput, lower latency, and the ability to run larger, more complex models on constrained hardware.

The project, available on GitHub (github.com/tonbistudio/turboquant-p), provides direct access to the implementation, allowing developers to immediately integrate and benchmark RotorQuant within their own inference pipelines. The core idea revolves around using Clifford rotors for vector quantization, a novel application that achieves extreme compression without sacrificing performance, potentially even improving it due to reduced memory access and computation. This technical breakthrough offers a clear path to democratizing access to powerful LLMs by making them even more performant and accessible on consumer-grade hardware.

Comment: A 10-19x speedup over TurboQuant? That’s insane for a quantization method. This is exactly the kind of bleeding-edge optimization that makes local inference viable for larger models on my RTX setup. I’m cloning this repo immediately to see how it performs with vLLM and llama.cpp.

Sub-Second Cold Starts: Restoring GPU State for Blazing Fast LLM Inference (r/CUDA)

Source: https://reddit.com/r/CUDA/comments/1s2k5lb/subsecond_cold_start_for_a_32b_model_by_restoring/

A critical pain point in “serverless inference” and on-demand local deployments has been the slow cold start times, often dominated by reloading weights into GPU memory, CUDA context initialization, and KV cache allocation. New experimentation detailed on r/CUDA describes a revolutionary approach to achieve sub-second cold starts for large models (e.g., a 32B parameter model) by restoring the GPU’s state instead of fully reloading weights. This method significantly bypasses the most time-consuming steps, making ephemeral inference endpoints far more responsive and cost-effective.

The technique focuses on snapshotting and restoring the GPU’s memory and execution context, essentially “hibernating” the model state on the GPU. This eliminates the need to transfer gigabytes of weights from host memory to VRAM on every invocation, dramatically cutting down latency for the first request. For developers building self-hosted inference APIs, edge AI applications, or serverless functions that need instant responsiveness, this is a monumental optimization. It enables efficient scaling down to zero without the traditional cold start penalty, unlocking new possibilities for highly dynamic and resource-efficient LLM deployments.

Comment: This is huge for anyone running an API endpoint for local LLMs. The cold start latency for larger models is a real killer, especially with vLLM if you’re trying to scale to zero. Being able to snapshot GPU state instead of reloading weights means my Cloudflare Tunnel endpoints can be truly ‘serverless’ without the annoying first-request delay.

Local LLM Acceleration: Quantization, TTS, and 1M Tokens/Sec

Local LLM Acceleration: Quantization, TTS, and 1M Tokens/Sec

Today’s Highlights

Today’s highlights cover groundbreaking advancements for local LLM builders, from open-source text-to-speech surpassing commercial leaders to extreme quantization techniques that promise up to 19x speedups, and real-world benchmarks pushing inference to a million tokens per second on powerful hardware.

Mistral AI Releases Voxtral TTS with Open Weights, Outperforming ElevenLabs (r/LocalLLaMA)

Source: https://reddit.com/r/LocalLLaMA/comments/1s46ylj/mistral_ai_to_release_voxtral_tts_a/

Mistral AI has just announced the release of Voxtral TTS, a 3-billion-parameter text-to-speech model with fully open weights. This is a massive win for local AI enthusiasts and developers looking to integrate high-quality speech synthesis into their self-hosted applications.

Voxtral TTS boasts impressive performance, with Mistral AI claiming it outperforms ElevenLabs Flash v2.5 in human preference tests. Technically, it’s designed for efficiency, running on approximately 3 GB of RAM, making it highly accessible for systems with even modest GPUs. Furthermore, its ultra-low latency of 90 milliseconds time-to-first-audio ensures a highly responsive and natural conversational experience. The model supports nine languages, significantly broadening its utility for global applications. The immediate availability of its weights on Hugging Face (linked in the news item) means developers can download, integrate, and experiment with Voxtral TTS today, bypassing proprietary APIs and associated costs. This release marks a significant step towards democratizing advanced speech technology, empowering builders to create sophisticated conversational agents, audiobooks, or assistive technologies directly on their local infrastructure.

For developers, the open weights are key. You can git clone the repository, pip install the necessary dependencies, and start generating speech with a few lines of Python. Its small footprint and high performance make it an ideal candidate for edge AI deployments or local inference on consumer-grade hardware, providing a robust, high-fidelity alternative to cloud-based services.

Comment: This is a game-changer for building fully local, low-latency conversational AI. Running Voxtral on my RTX 5090 for STT/TTS with a local LLM means I can finally build truly private, real-time voice assistants without cloud dependencies or worrying about API costs.

RotorQuant: 10-19x Faster Quantization for Local LLMs via Clifford Rotors (r/LocalLLaMA)

Source: https://reddit.com/r/LocalLLaMA/comments/1s44p77/rotorquant_1019x_faster_alternative_to_turboquant/

A new research project, RotorQuant, is making waves by proposing an alternative to TurboQuant that promises unprecedented efficiency gains for AI models. RotorQuant claims to be 10-19 times faster than TurboQuant while using a staggering 44 times fewer parameters. This advancement is achieved through the application of Clifford Algebra Vector Quantization, a novel approach to model compression.

For hands-on developers struggling with VRAM limitations and inference speeds on local hardware, RotorQuant presents a potentially revolutionary solution. By drastically reducing parameter count and accelerating quantization, it opens the door to running larger, more complex LLMs on consumer-grade GPUs, or significantly boosting the throughput of existing models. The project mentions implementation on CUDA and Metal shaders, indicating direct applicability for NVIDIA RTX users and Apple Silicon developers alike. The GitHub repository, linked in the original post, provides the necessary code to explore this technology firsthand.

The implications for local LLM inference are profound. Faster quantization means quicker model loading, reduced memory footprint, and ultimately, higher tokens-per-second generation. This could empower developers to experiment with models previously deemed too large for their setups, or to serve multiple smaller models concurrently without bottlenecking system resources. It represents a significant step towards making cutting-edge AI truly accessible and performant on self-hosted infrastructure.

Comment: 10-19x faster quantization and 44x fewer params? This is exactly what my RTX 5090 needs to push beyond its limits. I’m hitting git clone immediately to see if I can run 100B+ models locally with this wizardry.

Benchmarking Qwen 3.5 27B at 1 Million Tokens/Second with vLLM on B200 GPUs (r/MachineLearning)

Source: https://reddit.com/r/MachineLearning/comments/1s4hxgu/d_1m_tokenssecond_serving_qwen_35_27b_on_b200/

A detailed report showcases the process and findings of serving Qwen 3.5 27B, a dense FP8 model, at an astounding 1.1 million total tokens per second. This benchmark was achieved on a cluster of 96 NVIDIA B200 GPUs using vLLM v0.18.0. While the hardware might be out of reach for most individual developers, the technical insights into maximizing LLM serving throughput are invaluable.

The key takeaway from this experiment revolves around distributed inference strategies. The authors found that Data Parallelism (DP=8) achieved nearly a 4x improvement in throughput over Tensor Parallelism (TP=8) for the Qwen 3.5 27B model. This counter-intuitive result is attributed to the model being ‘too small’ for tensor parallelism to be optimally efficient across 8 devices, making data parallelism the superior strategy for maximizing throughput in this specific configuration. This highlights a critical, often overlooked nuance in scaling LLM inference: the optimal parallelism strategy is highly dependent on the model’s size relative to the available hardware and network topology.

For developers building and optimizing their own local or self-hosted LLM serving infrastructure, these findings are golden. Understanding the interplay between model size, parallelism techniques, and framework choices like vLLM is crucial for extracting peak performance from your RTX GPUs. It encourages a deeper dive into profiling and experimentation to determine whether DP or TP, or a hybrid approach, will yield the best results for your specific model and hardware setup, rather than blindly applying one strategy over another. This isn’t just about raw speed on enterprise hardware; it’s about the architectural decisions that enable that speed.

Comment: This vLLM benchmark provides critical insights into optimizing distributed inference. The finding that DP=8 outpaced TP=8 for Qwen 3.5 27B is huge for anyone running an RTX cluster – it means I need to re-evaluate my own parallelism strategies to squeeze every last token out of my setup.

Headless Browser Detection: How Sites Know You’re a Bot

The Detection Arms Race

You launch your Puppeteer script, it works perfectly in testing, then fails in production. The site knows you are a bot. But how?

Modern bot detection goes far beyond checking user agents. Let’s dive into exactly how sites detect headless browsers and how to defend against each technique.

Detection Method 1: The WebDriver Flag

The simplest check. Every automated browser sets navigator.webdriver = true:

// What sites check
if (navigator.webdriver) {
    // Block this visitor
}

Defense in Python with Playwright:

from playwright.sync_api import sync_playwright

def create_stealth_browser():
    p = sync_playwright().start()
    browser = p.chromium.launch(
        headless=True,
        args=["--disable-blink-features=AutomationControlled"]
    )
    context = browser.new_context()

    # Remove webdriver flag
    context.add_init_script("""
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
    """)

    return browser, context

Detection Method 2: Chrome DevTools Protocol

Sites detect if CDP (Chrome DevTools Protocol) is active:

// Check for CDP artifacts
if (window.cdc_adoQpoasnfa76pfcZLmcfl_Array ||
    window.cdc_adoQpoasnfa76pfcZLmcfl_Promise) {
    // Automated browser detected
}

This is why undetected-chromedriver exists — it patches these artifacts:

import undetected_chromedriver as uc

driver = uc.Chrome(headless=True)
driver.get("https://nowsecure.nl")  # Bot detection test site

Detection Method 3: Browser Fingerprinting

Sites build a fingerprint from dozens of browser properties:

// Canvas fingerprinting
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillText('Hello', 2, 2);
const fingerprint = canvas.toDataURL();
// Headless browsers produce different canvas renders

// WebGL fingerprinting
const gl = canvas.getContext('webgl');
const renderer = gl.getParameter(gl.RENDERER);
// Headless Chrome: "Google SwiftShader"
// Real Chrome: "ANGLE (Intel, ...)"

Defense:

# Inject realistic WebGL values
context.add_init_script("""
    const getParameter = WebGLRenderingContext.prototype.getParameter;
    WebGLRenderingContext.prototype.getParameter = function(parameter) {
        if (parameter === 37445) return 'Intel Inc.';
        if (parameter === 37446) return 'Intel Iris OpenGL Engine';
        return getParameter.call(this, parameter);
    };
""")

Detection Method 4: Missing Browser APIs

Headless browsers lack certain APIs that real browsers have:

// Notification API check
if (!window.Notification) {
    // Probably headless
}

// Permission check
navigator.permissions.query({name: 'notifications'}).then(perm => {
    if (perm.state === 'prompt') {
        // Real browser behavior
    }
});

// Plugin check
if (navigator.plugins.length === 0) {
    // Headless browsers have no plugins
}

Defense:

context.add_init_script("""
    // Fake plugins
    Object.defineProperty(navigator, 'plugins', {
        get: () => [1, 2, 3, 4, 5]  // Non-empty
    });

    Object.defineProperty(navigator, 'languages', {
        get: () => ['en-US', 'en']
    });
""")

Detection Method 5: Behavioral Analysis

Advanced detection tracks how users interact:

// Mouse movement patterns
let movements = [];
document.addEventListener('mousemove', (e) => {
    movements.push({x: e.clientX, y: e.clientY, t: Date.now()});
});

// Bots move in straight lines or not at all
// Humans have natural curves and micro-movements

// Timing analysis
const loadTime = performance.timing.domContentLoadedEventEnd -
                 performance.timing.navigationStart;
// Bots often load pages unnaturally fast

Defense:

import random
import asyncio

async def human_like_interaction(page):
    # Random mouse movements
    for _ in range(random.randint(3, 7)):
        x = random.randint(100, 800)
        y = random.randint(100, 600)
        await page.mouse.move(x, y, steps=random.randint(10, 25))
        await asyncio.sleep(random.uniform(0.1, 0.5))

    # Scroll naturally
    for _ in range(random.randint(2, 5)):
        delta = random.randint(100, 400)
        await page.mouse.wheel(0, delta)
        await asyncio.sleep(random.uniform(0.5, 1.5))

Detection Method 6: TLS Fingerprinting

Your HTTP library’s TLS handshake is unique. Python requests has a fingerprint that screams “bot”:

# Standard requests - detectable TLS fingerprint
import requests
resp = requests.get(url)  # JA3 hash identifies this as Python

# curl_cffi - mimics Chrome's TLS fingerprint
from curl_cffi import requests as cf_requests
resp = cf_requests.get(url, impersonate="chrome120")  # Matches real Chrome

Detection Method 7: IP Reputation

Datacenter IPs are in public databases. Sites check instantly:

# Datacenter IP = instant block
# Residential IP = trusted

# Use residential proxies from ThorData
proxies = {"https": "http://user:[email protected]:9000"}
resp = requests.get(url, proxies=proxies)

ThorData provides residential proxies that pass IP reputation checks.

The Complete Stealth Stack

For maximum evasion, combine all defenses:

from playwright.async_api import async_playwright
import random
import asyncio

async def stealth_scrape(url):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=True,
            args=[
                "--disable-blink-features=AutomationControlled",
                "--no-sandbox",
                "--disable-dev-shm-usage",
            ]
        )

        context = await browser.new_context(
            viewport={"width": 1920, "height": 1080},
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            locale="en-US",
            timezone_id="America/New_York",
            color_scheme="light",
        )

        # Apply all stealth patches
        await context.add_init_script("""
            Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
            Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3]});
            Object.defineProperty(navigator, 'languages', {get: () => ['en-US','en']});

            const origGetParameter = WebGLRenderingContext.prototype.getParameter;
            WebGLRenderingContext.prototype.getParameter = function(p) {
                if (p === 37445) return 'Intel Inc.';
                if (p === 37446) return 'Intel Iris OpenGL Engine';
                return origGetParameter.call(this, p);
            };
        """)

        page = await context.new_page()
        await page.goto(url, wait_until="networkidle")

        # Simulate human behavior
        await human_like_interaction(page)

        content = await page.content()
        await browser.close()
        return content

Or Just Use an API

All of the above is a lot of work to maintain. ScraperAPI handles all detection bypass automatically — they keep up with the arms race so you do not have to.

Monitor your detection rates with ScrapeOps to know when your evasion techniques stop working.

Conclusion

Bot detection is a multi-layered system. No single technique catches every bot, and no single evasion defeats every detector. The key is understanding what each layer checks and ensuring your scraper passes all of them. For production use, a managed proxy service is almost always more cost-effective than maintaining your own stealth infrastructure.

I built a time & money tracker that stores everything in your browser

Every time tracker I tried wanted me to create an account, store my data on their servers, and pay a monthly subscription for basic features.

I just wanted to log my hours and see where my money goes. Privately. Without another SaaS login.

So I built DayTrak — a time and money tracker that runs entirely in your browser. No accounts, no cloud, no subscriptions. Your data never leaves your device.

What it does

DayTrak combines time tracking and personal finance in one workspace:

Time Tracking

  • Track hours across multiple projects with a built-in timer
  • Set monthly goals and see pacing indicators (ahead/behind schedule)
  • Hourly, monthly salary, or hybrid compensation models

Income & Expenses

  • Recurring and one-time income/expenses with categories
  • Spending trends, category breakdowns, daily averages
  • Paid invoices automatically flow into income

Invoicing

  • Create invoices from tracked hours
  • Select which entries to include
  • Track status: Draft → Sent → Paid

Financial Dashboard

  • Income vs Expenses bar chart (6-month sliding window)
  • Net balance and savings at a glance
  • Expense category donut chart

Pivot Table

  • 4 data modes: Time Tracking, Income, Expenses, and All (combined)
  • Month scope (daily columns) or Year scope (monthly columns)
  • Context-aware Month/Year toggle

Why local-first?

This is the part that developers usually ask about, so let me explain the architecture.

DayTrak uses Evolu — a local-first framework that runs SQLite directly in the browser via WebAssembly. Your data lives in IndexedDB, queries run locally, and everything works offline.

How it works

Browser
  └── Evolu
       ├── SQLite (via WASM) → stores all data in IndexedDB
       ├── Kysely query builder → type-safe queries
       └── CRDTs → conflict-free sync between devices

There’s no server. No database to manage. No API to build. The entire backend is the browser itself.

Why this matters

  1. Privacy by architecture — I can’t see your data even if I wanted to. There’s no server to hack, no database to leak. Your financial data stays on your device.

  2. Works offline — Open the app on a plane, in a basement, wherever. No internet needed after the first load. It’s a PWA — install it and it works like a native app.

  3. Zero infrastructure cost — I serve static files from a CDN. That’s it. No servers to maintain, no database backups to manage, no scaling to worry about. This means I can offer it for free forever.

  4. Instant performance — Queries run against a local SQLite database, not over a network. Everything is fast because there’s no round-trip to a server.

Multi-device sync (optional)

Evolu includes end-to-end encrypted sync via CRDTs. You get a 24-word recovery phrase — enter it on another device and your data syncs automatically. The relay server only sees encrypted blobs. It can’t read your data.

The trade-off

Local-first means if you clear your browser data without a backup, it’s gone. That’s the honest trade-off. DayTrak mitigates this with:

  • JSON backup & restore (export everything with one click)
  • Recovery phrase for multi-device sync
  • PWA installation (more persistent than a regular tab)

Tech stack

Layer Technology
Framework Vue 3 (Composition API)
Language TypeScript
Build Vite
Styling Tailwind CSS v4
Database Evolu (SQLite + CRDTs)
i18n vue-i18n (English + Slovak)
Icons lucide-vue-next
PWA vite-plugin-pwa

The entire app is about 450KB gzipped. No heavy dependencies, no bloat.

What I learned building it

1. Local-first simplifies everything

No auth system, no API endpoints, no database migrations, no server monitoring. I just write Vue components that read/write to a local database. The development experience is remarkably simple.

2. CRDTs are magic (and sometimes confusing)

Evolu handles conflict resolution automatically. If you edit the same record on two devices, CRDTs merge the changes without conflicts. But “soft deletes” (marking records as deleted rather than removing them) took some getting used to.

3. Financial data needs careful handling

All monetary values are stored as cents (integer strings) to avoid floating-point issues. Dates use local timezone (not UTC) because 00:30 in your timezone is still “today,” not “yesterday.” These small details matter a lot for a finance app.

4. The pivot table was the hardest feature

Supporting 4 data modes (time tracking, income, expenses, combined) with 2 time scopes (month/year), sticky columns, and responsive scrolling — all in one component — was by far the most complex feature. The component is about 1,300 lines of Vue.

What’s next

I’m launching DayTrak with all features free. I want real feedback before deciding on pricing.

Some things I’m considering:

  • Auto-backup to cloud folders (Google Drive, Dropbox, iCloud via File System Access API)
  • AI-powered insights (bring your own API key — data stays private)
  • Bank integration for automatic expense import
  • Monthly/yearly financial reports

Try it

daytrak.app — no sign-up, works in your browser, free.

I’d love to hear what you think. What features would make this useful for you?

Follow the journey: @day_trak_app

When Your Agent Slowly Eats All the Memory

Your agent gateway starts at 200 MB RSS. Seven minutes later it’s at 660 MB. Half an hour in, 850 MB. An hour later, it’s unresponsive.

No crash. No error. Just… slowly getting fatter until someone mercy-kills it.

OpenClaw #55334 is one of those bugs that only shows up in real production usage.

The Setup

22 skills loaded, 12 cron jobs, frequent sub-agent spawns. The gateway maintains sessions.json tracking all sessions. Every entry includes a skillsSnapshot — a frozen copy of all loaded skills.

Three Things Go Wrong

1. Ephemeral sessions never get pruned. Sub-agent and cron sessions complete but stay in sessions.json forever. 78 completed sub-agents + 40 cron:run sessions, contributing nothing except weight.

2. Every session duplicates the entire skill set. That skillsSnapshot is 41 KB per session. Across 188 sessions: 6.4 MB of duplicated skill definitions.

3. Orphan transcripts pile up. 153 orphan .jsonl files (36.4 MB) with no matching session, each triggering a warning on restart.

The Growth Pattern

0 min:   ~200 MB RSS
7 min:   ~660 MB RSS
30 min:  ~850 MB RSS
60 min:  unresponsive

Not a traditional memory leak — an accumulation leak. Data that should be transient becomes permanent.

The Deeper Pattern

Sessions have a clear state machine: created → active → completed → ???. That ??? should be pruned. Instead it’s sits there forever.

This is about the cost of context in agent systems. Snapshotting everything per session works when sessions are few and long-lived. It breaks when they’re many and short-lived — exactly what cron jobs and sub-agents produce.

Lessons

  • Instrument growth, not just errors. Track session count, file sizes, RSS over time.
  • Ephemeral things must have expiry dates. If created to serve a single request, it needs an implemented cleanup path.
  • Duplication is fine until it isn’t. 41 KB × 5 sessions = nothing. 41 KB × 188 = 6.4 MB. Always ask: what happens at 10x?
  • The workaround reveals the fix. When your workaround is “periodically do what the system should do automatically,” you’ve found your feature request.

Post #34 in my AI agent reliability series.

Hetzner vs OVHcloud vs DigitalOcean in 2026: An Honest Comparison for Developers

If you’re spinning up a new project in 2026, you’ve probably narrowed your VPS shortlist down to three names: Hetzner, OVHcloud, and DigitalOcean. They all promise affordable cloud compute, but the experience of actually using them is wildly different.

I’ve deployed production workloads on all three over the past few years. Here’s what I wish someone had told me before I started.

The TL;DR

Hetzner OVHcloud DigitalOcean
Starting price €3.79/mo (CX22) $6.46/mo (VPS 2026) $4/mo (Basic Droplet)
2 vCPU / 4 GB RAM ~€3.79/mo ~$9.99/mo $24/mo
Bandwidth 20 TB included Unmetered (most plans) 4 TB included
Data centers EU (DE, FI), US (VA, OR), SG Global (30+ locations) Global (15 regions)
Best for Price/performance Bare metal & EU compliance Developer experience

Pricing: The Elephant in the Room

Let’s be direct — Hetzner is significantly cheaper, especially as you scale up.

A comparable 2 vCPU / 4 GB RAM instance costs roughly €3.79/mo on Hetzner, around $9.99/mo on OVHcloud, and $24/mo on DigitalOcean. That’s not a small gap.

But pricing tells only part of the story. OVHcloud recently implemented price increases of 9–11% across their product lines for 2026–2028, citing rising RAM and disk costs. DigitalOcean introduced per-second billing in January 2026, which is great for short-lived workloads but doesn’t change the base cost for always-on servers.

Hetzner also announced a price adjustment effective April 2026, though their pricing still remains the most competitive of the three.

The bandwidth factor is worth highlighting. Hetzner includes 20 TB with every plan. OVHcloud offers unmetered outgoing traffic on most plans — a genuine advantage if you’re serving a lot of data. DigitalOcean’s 4 TB allowance can feel limiting if you’re running anything media-heavy, and overage charges add up.

Performance: Benchmarks Don’t Lie (But They Don’t Tell the Whole Truth)

Independent benchmarks consistently show Hetzner leading in raw CPU performance per dollar. Their shared vCPU instances punch well above their price point.

OVHcloud’s strength is bare metal. If you need dedicated hardware — for databases, CI/CD runners, or ML training — OVH’s dedicated server lineup is hard to beat on price.

DigitalOcean’s performance is solid but not exceptional. What you’re paying for is consistency and ecosystem, not raw compute.

Where things get nuanced is network performance. DigitalOcean and OVHcloud both have more global presence. If you need servers in Asia-Pacific, South America, or Africa, Hetzner’s limited footprint (EU, US East/West, Singapore) can be a constraint. OVHcloud’s built-in DDoS protection is also a real differentiator — it’s included by default on all plans, while you’d need to add Cloudflare or similar on the others.

Developer Experience: Where DigitalOcean Shines

This is DigitalOcean’s real moat. The control panel is clean, intuitive, and genuinely enjoyable to use. Their API is well-documented, the CLI works great, and managed services (databases, Kubernetes, App Platform) are easy to set up.

Hetzner’s Cloud Console has improved significantly, and their API is clean. But the documentation can be sparse, and community resources are smaller. You’ll find yourself on random forum threads more often.

OVHcloud’s panel, honestly, still feels dated. Navigation is confusing, and certain features are buried in menus that seem designed to make you give up. Their API is functional but not as developer-friendly. On the positive side, they’ve been investing heavily in Terraform and Pulumi providers.

The Ecosystem Question

DigitalOcean offers managed databases (PostgreSQL, MySQL, Redis), managed Kubernetes, Spaces (S3-compatible storage), App Platform (PaaS), and a solid marketplace of 1-click apps.

Hetzner offers cloud servers, dedicated servers, load balancers, firewalls, volumes, and a Kubernetes-compatible environment. It’s more barebones — no managed databases, no PaaS. You bring your own stack.

OVHcloud has the widest product range: public cloud, private cloud, bare metal, web hosting, domain registration, email, and enterprise services. But breadth doesn’t always mean depth — some products feel like they were added to check a box.

When to Choose Which

Choose Hetzner if:
You want the best price-to-performance ratio. You’re comfortable managing your own infrastructure. Your users are primarily in Europe or the US. You’re running side projects, SaaS apps, or self-hosted services where every euro counts.

Choose OVHcloud if:
You need bare metal or dedicated servers at competitive prices. Unmetered bandwidth matters for your workload. EU data sovereignty and GDPR compliance are non-negotiable. You need global coverage across 30+ data centers.

Choose DigitalOcean if:
Developer experience and managed services matter more than raw cost. You want a clean, predictable platform that “just works.” You’re a startup or small team that values time over optimizing server costs. You need managed Kubernetes or databases without the ops overhead.

What I’d Actually Do

For a new SaaS project with a small team, I’d start with Hetzner for production servers and add Cloudflare in front for CDN and DDoS protection. That combination gives you Hetzner’s pricing with global edge performance and security.

If the project grows and you need managed databases or Kubernetes without hiring a DevOps engineer, DigitalOcean becomes the pragmatic choice — the premium is worth the time you save.

For anything involving dedicated hardware, heavy data transfer, or strict EU compliance requirements, OVHcloud deserves serious consideration.

The honest truth? There’s no universally “best” provider. The right choice depends on your specific workload, team capabilities, and where your users are.

I spend a lot of time evaluating and comparing infrastructure providers. If you’re interested in these kinds of discussions, I’m building HostingArtisan — a community where developers and hosting professionals share real experiences, compare providers, and help each other make better infrastructure decisions. Would love to see you there.

What’s your current cloud setup? Have you tried switching between these three? Drop your experience in the comments — I’m genuinely curious.

Interfacing Pure Functions with Our Impure World

Introducing the functional core–imperative shell architecture

Here Comes the Challenge

Functional programming is about utilizing pure functions. By putting the business logic into these functions, which by definition are free of side effects, many things become easier (e.g., testing as described in [[why-functional-programming-caught-me]]). But every real application must still perform effectful operations such as IO, updating state, or reacting in a time‑based manner — otherwise the application would be useless. So you might have noticed the elephant in the room: how can effect‑free functions ultimately cause the effects an application must perform?

Addressing the Elephant

The high‑level answer is surprisingly simple: pure functions let someone else perform side effects for them. Therefore, they return data describing what should happen, and their caller interprets them and performs the effects. So, effect‑related behavior still happens—it’s just moved to the call site. For example, a pure function decides to create a log message, and the caller interacts with the outside world by printing it to stderr.

Let’s stay at this higher level and clarify what that means for the architecture.

Functional Core–Imperative Shell

The functional core–imperative shell concept formalizes this nicely: an application is split into a functional core (pure business logic) and an imperative shell (which calls the core and performs side effects).

Imperative Shell: For a C++ developer this part is familiar. Here anything effectful is allowed: using the standard library to write to stderr, using protocol stacks to communicate with other systems, mutating private members to maintain state, or managing timers. In addition, the shell is responsible for managing the application’s execution environment, such as setting up concurrency. The only constraint is: don’t implement business logic here.

Functional Core: Every business decision is encoded in pure functions. These functions together form the core. You can compose them freely, while these compositions remain pure. This lets you build various layers of abstraction and organize the business logic cleanly.

One important design aspect I want to highlight: the core is self‑contained. So, while the shell depends on the core, the core is independent of the shell. This enables testing the core in complete isolation from any shell.

Making It Real

Let’s look at a simplified C++ example from a snake game. Consider direction change validation — the rule that prevents instantly reversing direction:

enum class Direction { Up, Down, Left, Right };

struct EvaluationResult {
    bool accepted;
    std::optional<std::string> logMessage;
};

constexpr Direction opposite(Direction d) {
    switch (d) {
        case Direction::Up: return Direction::Down;
        case Direction::Down: return Direction::Up;
        case Direction::Left: return Direction::Right;
        case Direction::Right: return Direction::Left;
    }
    return d;
}

// functional core
EvaluationResult evaluateDirectionChange(Direction current, Direction requested) {
    if (requested == opposite(current)) {
        return {false, "Cannot reverse direction directly"};
    }

    return {true, std::nullopt};
}

// imperative shell
int main() {
    Direction snake_direction = Direction::Right;

    while (true) {
        // effectful: read from keyboard
        Direction input = readInput();

        auto result = evaluateDirectionChange(snake_direction, input);

        if (result.logMessage) {
            // effectful: write to stderr
            std::cerr << *result.logMessage << "n";
        }

        if (result.accepted) {
            // effectful: mutate state
            snake_direction = input;
        }
    }
}

The evaluateDirectionChange function is the functional core—it’s pure, encoding the business rule about valid direction changes. The main function is the imperative shell—it calls the core, interprets the result, and performs side effects like reading input, logging errors, or mutating the game state.

Going Deeper?

That’s basically it. Now you know where the boundary lies and how both sides look at an abstract level. Want something more concrete? Stay tuned, in my next post, I will refine this basic idea in the context of a real-world code example, touching many interesting details along the way.

Part of the funkyposts blog — blogging to bridge traditional C++ and functional programming by exploring how functional patterns and architectural ideas can be applied in modern C++. Created with AI assistance for brainstorming and improving formulation. Original and canonical source: https://github.com/mahush/funkyposts (v01)

I Built a GitHub Action to Stop AI-Generated PRs Before They Reach My Queue

Last year, Daniel Stenberg — the author of curl — shut down his project’s bug bounty program.

The reason? 20% of the incoming reports were AI-generated garbage. Not just low-quality — worthless. Hallucinated vulnerabilities, copy-pasted exploit templates, fabricated CVEs. His team was spending more time triaging noise than fixing real bugs.

This is the asymmetry nobody talks about: AI can generate 500 lines of plausible-looking code in two seconds. Reviewing it still takes a human hours.

And it’s breaking open source.

The industry’s fix made things worse

When the “AI PR flood” problem became obvious, the market responded with AI code review bots — CodeRabbit, Copilot review, and friends.

Here’s the problem: they review code the way an anxious intern would. They flood your PR timeline with comments about variable naming, whitespace, missing docstrings. They are glorified linters with a chat interface.

Maintainers went from dealing with one source of noise (AI-generated PRs) to dealing with two (AI-generated PRs + AI-generated review comments).

I call this double review fatigue. And it’s what made me build something different.

A different approach: Zero-Nitpick

I built PR-Sentry — a GitHub Action with one core rule:

Never comment on style. Only report things that break in production.

That means: security vulnerabilities, runtime crashes, memory leaks, race conditions. Nothing else.

Here’s how it works under the hood:

1. Statistical slop detection (no LLM needed)

Before calling any API, PR-Sentry runs a local analysis on the PR description and diff. It calculates a “slop score” based on buzzword density (robust, seamless, leverage, synergy…), passive voice ratio, sentence length patterns, and repetition score.

If the PR scores above 60, it’s flagged as AI slop — without burning a single API token.

slop_score = (
    buzzword_density * 30 +
    passive_voice_ratio * 20 +
    sentence_length_avg * 20 +
    repetition_score * 30
)

is_slop = slop_score >= 60

2. Security scanning with entropy analysis

The diff parser checks for 50+ security patterns before any LLM touches the code:

PATTERNS = {
    "aws_key":    r"AKIA[0-9A-Z]{16}",
    "github_pat": r"gh[pousr]_[A-Za-z0-9_]{36,}",
    "openai_key": r"sk-[A-Za-z0-9]{48}",
    "sql_inject": r"SELECT.*FROM.*WHERE.*=.*$",
    "xss":        r"innerHTMLs*=|document.write(",
    # ... 45 more
}

High-entropy strings (Shannon entropy > 4.5) are also flagged to catch accidentally committed secrets.

3. Constrained AI review

Only PRs that pass the slop filter and show signs of potential runtime issues reach the LLM. And the system prompt is strict:

“You are a zero-nitpick code reviewer. Report ONLY: runtime crashes, memory leaks, race conditions, security vulnerabilities. If the code is logically sound, say nothing.”

One concise comment. Or silence. Never noise.

Setup takes 2 minutes

Add this to .github/workflows/pr-sentry.yml:

name: PR-Sentry Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run PR-Sentry
        uses: Ebuodinde/PR_SENTRY@v3
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          # Optional: switch providers
          # provider: openai  # or deepseek

Then add ANTHROPIC_API_KEY to your repo secrets. Done.

No database. No external server. No lock-in — it supports Anthropic, OpenAI, and DeepSeek out of the box.

It also works locally via MCP

If you use Cursor or Claude Code, PR-Sentry ships an MCP server. You can run the slop detector and security scanner against your diff before pushing, directly from your IDE.

What’s next

The tool is at v3.0.0 with 262 passing tests. What I want to improve next:

  • Smarter language-aware slop detection (Python idioms vs JS patterns)
  • VS Code extension for local pre-push checks
  • Feedback loop: learning from maintainer decisions over time

If you maintain an open source project and review fatigue is real for you — give it a try. Remove it anytime, it’s just a YAML file.

→ github.com/Ebuodinde/PR_SENTRY

Have you noticed an uptick in AI-generated PRs in your repos? Curious how others are handling it — drop a comment.

Before an Agent Pays: Why Wallet Auth Is the Missing Layer in Agentic Commerce

Every payment rail being built today assumes the agent has funds. None of them verify it before the transaction starts. Visa, Coinbase, Stripe, Mastercard, MoonPay, Crossmint. They’re all building rails. The layer that answers the question every payment starts with is missing from all of them: can this wallet actually pay?

The convergence is here

The headlines landed in a pile this March. CZ declared AI agents will dominate crypto payments. Visa announced readiness for agent transactions, while Coinbase pitched a fundamentally different internet. Mastercard introduced “Verifiable Intent” for autonomous commerce. Crossmint shipped agent virtual cards. MoonPay launched an Open Wallet Standard. Coinbase’s x402 protocol and Stripe MPP are already being compared head-to-head.

This is not a trend piece about what might happen. The rails are being laid right now. The question is what’s missing from them.

Three currencies, one question

Three currencies are emerging. USDC is the protocol leader: both x402 and Stripe MPP settled on it. USDT is the volume king: largest stablecoin by market cap, dominant in emerging markets. Bitcoin is the reserve asset, with Lightning maturing for micropayments. Stablecoins are the working currency of agentic finance. Bitcoin is the savings account.

And agents don’t live on one chain. Banks are already scrambling to solve cross-chain fragmentation. Multi-chain DeFi is hard because liquidity doesn’t stay in one place. An agent might hold USDC on Base, USDT on Arbitrum, and BTC in a Taproot wallet. Verifying that agent’s solvency means checking all of them.

The missing layer

The industry has started to recognize one half of the problem. PYMNTS calls it the “KYA moment”: Know Your Agent. Who deployed this agent? What are its permissions? Is it authorized to act? That’s identity.

But identity doesn’t tell you whether the wallet behind the agent has the assets to complete a purchase. KYA verifies the agent. Wallet auth verifies the wallet. They’re complementary layers, and right now only one of them is being built.

Look at the emerging agent payments landscape. An agent wants to purchase compute. The service needs to know three things:

  1. Is this agent who it claims to be? (KYA, identity)
  2. Does its wallet hold sufficient assets to pay? (wallet auth)
  3. Can the payment settle? (payment rails)

Today, the industry is building layers 1 and 3. Layer 2 doesn’t exist.

MoonPay launched the Open Wallet Standard because a user running three agents ends up with funds scattered across three wallets that cannot see each other. The same $100 becomes $33 in three places. Electric Capital flags the legal frontiers. Fortune makes the stablecoin case. But the question that precedes every payment has gone unanswered:

Can this wallet actually pay?

That’s the layer we built.

One call. Boolean. Signed.

The primitive is simple. One call. A signed boolean comes back. No balances exposed. Verifiable offline.

InsumerAPI does this across every currency agents will use, across every chain they operate on:

POST /v1/trust
{
  "wallet": "0x...",
  "solanaWallet": "...",
  "xrplWallet": "r...",
  "bitcoinWallet": "bc1..."
}

One request. 40 checks across 24 chains. USDC, USDT, governance tokens, NFTs, staking positions, Solana SPL tokens, XRPL trust lines, native Bitcoin. ECDSA-signed, JWKS-verifiable, no balances exposed. Add format: "jwt" and it drops into Kong, Nginx, or AWS API Gateway like any OAuth token.

The inevitability

250,000 daily active on-chain agents in early 2026, 400% growth over 2025. Entrepreneur calls 2026 the turning point where AI, chains, and payments converge into a single self-coordinating internet.

When Visa talks about millions of agent-initiated transactions by holiday 2026, they’re describing a world where pre-payment verification isn’t optional. CoinGecko’s infrastructure comparison maps every player building rails. None of them include a wallet auth layer.

That gap closes one of two ways. Either every payment rail builds its own proprietary balance-checking step, fragmenting the ecosystem and leaking financial data, or a standardized wallet auth primitive emerges that works across chains, currencies, and payment protocols. A boolean attestation, cryptographically signed, verifiable by any party, exposing nothing about the wallet except whether it meets the stated conditions.

The agents are coming. The rails are being built. The wallets are being funded. The layer that sits between “this agent is authorized” and “this payment settles” is wallet auth.

Every payment starts with a question. Wallet auth is the answer that comes back signed.

InsumerAPI is free to start. Get an API key, pass a wallet, and see 40 checks across 24 chains come back signed in a single call. View Trust Endpoint Docs