Vol. I No. 1 Control Plane Edition Published this morning

The Daily A vesper bat, flapping very slowly A small brown vesper bat with membranous wings that rise and fall in a slow loop. Transcript

In which spoken words are captured, tallied by the second, and charged to the proper account — with every token inspected at the door.

Ed25519 signatures only Single-use tokens Append-only ledger SQLite · WAL

Speak Into the Paper

No account. Your microphone, transcribed here, now.
The needle rests 0.0 / 60s Press the seal to speak

Your browser will ask for microphone access. Nothing is stored.

Twenty seconds of audio every thirty seconds, per visitor — so speaking without pause means a ten-second wait between takes. The limit is enforced three times over: this page asks a gateway for a grant, the grant itself carries the twenty-second budget inside its signature, and the demo account has a finite credit balance. Bypassing the first still leaves the other two.

Two Houses, One Service

The counting-house keeps the books; the workshop does the listening.

Every voice that reaches this service arrives carrying a letter of introduction. The service itself is split in two, and the split is the whole design: the control plane knows who the customers are, what they have paid for and how much they have left, while the data plane knows only how to turn audio into text as fast as the hardware allows.

The counting-house holds customers, signing keys, plans, credit and usage history. Its traffic is low and transactional; it wants durability more than speed. The workshop holds none of that. It is latency-sensitive, it scales sideways, and when it needs to know whether a speaker may proceed, it asks.

The arrangement means a customer's own users never hold an account here. Their provider vouches for them — and the vouching is cryptographic, not a matter of trust.

Continued in “The Gauntlet”, right →

The Sealed Letter

Why we can read a customer's seal but never forge one.

Each customer holds an ed25519 private key. We hold only the public half. When one of their users wishes to dictate, the customer's own server signs a short-lived token stating who the user is and how many seconds they may spend.

Because the limits are inside the signature, they cannot be edited on the way through. A user who opens the console and changes max_audio_seconds from sixty to sixty thousand produces a token that no longer verifies.

The asymmetry is the point. A shared password would let us mint tokens in a customer's name; a public key lets us only ever check them.

A grant token's claims, as signed by the customer

Passage of a Single Word

Four parties, and only one of them holds a private key.

The user logs in with their own provider. That provider signs a grant. The user carries it to a transcription node, which checks it against the counting-house. Audio flows; seconds are tallied and charged as they are spoken, not merely at the end.

Should the counting-house fall silent, the workshop verifies signatures itself against keys it cached earlier, and keeps listening — recording what is owed for when the post resumes.

Take Note

  1. A refusal is honoured. If the counting-house says a customer is out of credit, the node refuses. It never guesses in the customer's favour.
  2. Silence is not a refusal. A timeout means no opinion was given, so the node decides for itself and settles up later.
The speaker no account with us Their provider holds the private key Transcription node audio in, text out Counting-house keys, plans, credit signed grant carried by the user verify limits + balance
The passage of one grant token — the seal never leaves the provider

The Gauntlet

Spoil the token and see which door refuses it

Every token runs the same seven checks in the same order, and stops at the first failure. The messages below are the ones the service actually returns. Introduce a flaw, then present the token.

    Read the Seal

    Paste any grant token — nothing leaves this page

    Decoding is not verification. A token's claims are plain text to whoever holds it, so reading them proves nothing — only checking the signature does. The key that check needs is the public one, which is why we publish it openly at /v1/customers/{slug}/jwks.json: it can confirm a seal, never counterfeit one. Only the private half must never leave the customer.

    Header

    Claims

    The Counting-House Ledger

    Nothing is ever edited — the balance is the sum

    Credit is not a number we overwrite. It is a column of entries, and the balance is whatever they add up to. When a customer disputes an invoice, every second can be accounted for by the rows that produced it.

    Credit ledger entries
    Entered Reason Reference Seconds
    Balance 0 seconds SUM(delta_seconds)

    Directory of Addresses

    Everything your servers will call, and what to present

    The Proving Room

    Run the real thing against a live account, from this page

    Paste an API key and this calls the service for real. Requests go straight from your browser to the address below and nowhere else — there is no server behind this page holding your key.

    Paste a key and press "Read my account".

    We never ask for your real private key. "Generate a test key" creates a throwaway pair inside your browser, registers only the public half, and signs with the private half without it ever leaving this tab — which is exactly the shape of the real integration.

    Working Demonstrations

    Clone one, add your key, speak into it

    Two complete integrations, each a few hundred lines with a microphone and a live transcript. They differ in one decision: whether the audio passes through your servers.

    Straight to the node

    Your server mints a grant; the browser streams directly onward. Audio never touches your infrastructure, so it costs you nothing per spoken second and adds no latency. Start here.

    git clone https://vesper.ninja/demos/demo-browser.git
    cd demo-browser
    cp .env.example .env    # your SLUG, KID, PRIVATE_KEY
    npm start               # no dependencies

    demo-browser.zip · no dependencies

    Through your own server

    The browser talks only to you. Your server holds the grant, relays the audio, and logs every transcript. Choose it when you need custody: records to retain, redaction, an audit trail.

    demo-proxy.zip · demo-proxy-go.zip

    Smaller still: customer-server is ninety lines that mint tokens and nothing else — git clone https://vesper.ninja/demos/customer-server.git

    Your private key lives in .env, which is gitignored in every one of them. It signs grants on your server and must never reach a browser.

    Take This Away

    The whole integration, in the language you already use

    How to Begin

    1. Take an account. You receive a dashboard login, an API key and an ed25519 keypair — or register a public key you generated yourself, and we never hold the private half at all.
    2. Sign grants. Your server authenticates your users however it already does, then signs a short token saying who may speak and for how long.
    3. Send them to us. Your users connect with that token. We check the seal, apply the limits and tally the seconds.

    The worked example is about ninety lines of Node, with no dependencies.

    Topping Up Mid-Sentence

    A speaker who runs out of seconds need not be cut off.

    A grant carries a budget of seconds. When it runs out the session ends — which, for someone dictating a letter, arrives at the worst possible moment. So a live session can be extended without reconnecting.

    Send a fresh grant over the open connection. The seconds are added to what the session already had, so you can hand out a minute at a time without tracking how much has been used.

    Extending a session that is already running

    The grant must name the same customer and the same end user. One of your users cannot extend another's session, and no one else's grant can move a session onto your account.

    Nothing is dropped: no reconnection, no gap in the audio, no missing words in the transcript.

    When the Post Fails

    A refusal from the counting-house is a decision and is always obeyed. A timeout is not: it means no decision was reached. The node then verifies the signature itself against cached public keys and keeps transcribing.

    Two things still hold offline: signatures are checked, and a token can still only be spent once on that node. What cannot hold is credit — the ledger is elsewhere. Seconds accrued are queued and settle when the post resumes.

    The trade is deliberate and it favours you: your users keep dictating through an interruption on our side, and the account is squared up afterwards rather than sessions being dropped.

    Notices & Small Print

    Limits to design around
    Token lifetime · 3600s ceiling
    Mint whatever lifetime you like; we decline to honour anything longer than an hour. Minutes are the sensible choice.
    Single use
    A token is spent on first use. Mint one per connection — a reconnect needs a fresh one, and so does each top-up.
    Extending a live session
    Send a fresh grant over the open connection and its seconds are added to the session. Same customer, same user, no reconnect.
    Clock skew · 60s
    Allowed either side of exp and nbf, so your signing server need not be perfectly in step with ours.
    Algorithm · EdDSA only
    ed25519 signatures. An alg of none, or an HMAC substitution, is refused before anything else is read.
    jti · required
    Without one a token cannot be single-use, and will be refused.
    Effective limit
    The least of what you granted, what your plan allows, and the credit remaining on your account.
    Key rotation
    Several signing keys may be active at once, so you can roll a new one out before retiring the old. We refuse to retire your last one.