Skip to main content
ZeroClick::Sellers::Middleware is plain Rack middleware, so it composes with Rails, Sinatra, Hanami and Roda alike. Nothing in it requires the rack gem — a Rack middleware is duck-typed — so mounting the guard adds no dependency to your bundle.

Meter

Guards a billable endpoint: verifies the signature, confirms the buyer can pay, and — if your app responds 2xx — settles the usage.

Every item needs an explicit quantity

Meter settles usage from what it declared, so each item must carry a definite quantity. Anything else has no settled amount to report, and inventing one would silently mis-bill a delivered 200 — the exact failure this SDK exists to prevent. It therefore raises when you build the middleware, not in production:
Both forms remain valid for guard, which only asks whether the buyer could pay. It is settlement that needs a definite number. Declare the fixed part in the middleware and report the variable part afterwards with report_usage.

Only delivered responses are billed

zc-usage is attached only when your app answers 2xx. A 4xx or 5xx gets no usage header, because the work was not delivered.

Identify

Guards a free endpoint that must still know which buyer is calling — a limits or account route. It makes no network call.
A signed anonymous probe (no zc-agent-id) is refused with a 402 carrying an empty usage list. That is the free identity-scoped refusal: on a pay-as-you-go seller the proxy answers it with a $0 identity challenge and retries with an agent attached, so the buyer is identified without being charged. On a plan-priced seller the buyer never reaches that challenge. Identity is checked before anything is priced, so an unidentified caller gets 401 bearer_required from ZeroClick and must register a credential first. Either way your endpoint’s contract is the same — it refused an unidentified caller — but the buyer-side recipe differs, so point plan-priced buyers at the seller’s /auth.md.

The verified caller

A guarded request carries what it proved on the Rack env:
Under Rails that is request.env["zeroclick.context"].

The request body

Verification covers the whole body, so the middleware must read it. It then replaces rack.input with a fresh stream over the same bytes, so your app reads the body normally. It deliberately does not call #rewind: Rack 3 dropped the requirement that input be rewindable, and against a streaming server #rewind either raises or silently does nothing — handing the application an empty body.

Mounting on a subset of routes

Rack middleware runs on every request through the stack. To guard only some paths, mount it on a scoped app rather than globally:
Under Rails, mount a guarded Rack app in routes.rb, or guard inside the controller with guard when routing is more naturally expressed there.

Ordering

Mount the guard above anything that reads or rewrites the request body, and above logging you want to carry the buyer id. Mount it below anything that must run for unauthenticated traffic too — health checks, and whatever terminates TLS or normalises the host.