The Usage & billing screen shows both halves of the tunnel’s money flow on one page: metered egress on the left, the wallet ledger on the right. Egress is what you consume; the ledger is the record of what that consumption cost and what you have paid in. The screen is backed by two procedures:

The wallet model

Tunnel billing is prepaid. You add credit to a wallet, and metered egress is debited against it. There is no invoice to settle at the end of the period; the balance is the running sum of everything the ledger has recorded. Two flows move money:
  • In — top-ups, taken through Razorpay, posted to the ledger as positive deltas.
  • Out — metered egress past the free tier, posted as negative deltas.
The ledger table (rnp_wallet_ledger) is the authoritative money trail. The egress panel is a usage view: it mirrors the same counters that the tunnel daemon (rnpd) bills from, so what the screen shows is what gets debited — but if the two ever disagree, the ledger is the record of account. All amounts in both procedures are integers in the minor unit of the currency (delta_minor, pricePerGbMinor). The console formats them for display. The currency is resolved in this order: the ledger’s currency, then the overview’s currency, then INR as a last fallback — so a ledger response that carries no currency is still rendered consistently with the header.

How egress is counted and attributed to an account

Usage is attributed per account. Each row of overview.topUsers carries: The counters live in Redis and are the same keys rnpd reads when it produces debits. That is the point of the panel: it is not a separate estimate computed for the UI, so you can read a number here and expect it to be the basis of the corresponding ledger entry. The billable-egress table hides rows whose egressBytes is zero. An account that has connected but moved no bytes this period will not appear. If no account has non-zero egress, the panel shows an empty state instead of a table of zeros. Egress is scoped to the billing period reported by overview.period, which the console prints in the page subtitle. Counters are per period, so the panel answers “what has this account used this period”, not lifetime usage.

The free tier and the per-GB price

The overview returns the two numbers that define the price curve:
  • freeGb — the allowance, in GB, before anything is billable.
  • pricePerGbMinor — the per-GB price in minor currency units, applied past that allowance.
The console renders them together in the subtitle: price/GB after N GB free. Both values come from the control plane on every load, so read them from the screen rather than assuming a figure — they are not hardcoded in the UI and this page deliberately does not restate them. Because the allowance is expressed in GB and the counter in bytes, the conversion from egressBytes to a currency amount happens in rnpd when it writes the debit. The screen does not perform that arithmetic and does not display a projected charge. If you want to know what an account has actually been charged, read its ledger entries.

Ledger entries: kinds, deltas, and references

Each entry in ledger.entries has this shape: Kinds the console styles explicitly: The kind set is not closed. Any other kind the control plane emits — an adjustment, a correction — still renders, with a neutral tag, and still counts toward the balance through its delta_minor. Do not infer direction from the kind: the sign of delta_minor is what decides whether an entry adds to or subtracts from the wallet, and it is what drives the + prefix and the colour in the Δ column. Entries are returned newest-first and the console asks for the latest 100. The screen is therefore a recent-activity view, not a full statement; a long history extends past what this page shows.

Top-ups and payment references

A top-up is a positive-delta entry of kind topup. Because top-ups are taken through Razorpay, the entry’s ref is the handle you use to tie a ledger credit back to the payment that produced it. The ledger does not constrain the reference’s format, and the console prints it verbatim (truncated with an ellipsis when it is too wide for the column), so treat it as an opaque string to match on rather than to parse. The same column carries the reference for debits, which is what lets you trace a specific egress entry back to the metering write behind it.

What the screen tells you when the balance runs out

The ledger records money movement; it does not itself gate traffic, and this screen exposes no enforcement control, no threshold and no cutoff toggle. What it gives you is the trail: credits in, debits out, and the reference for each. Practically, that means the page is a diagnosis surface, not a lever. If egress debits have consumed the credits, the fix visible from here is a further top-up, which will appear as a new topup entry once it posts.

Reconciling the ledger against usage counters

The two panels are deliberately independent reads of the same underlying facts, which makes reconciliation possible:
  1. Take an account’s egressBytes from the billable-egress panel.
  2. Find that account’s egress entries in the ledger for the same period (match on the shortened sub, then confirm with the full sub from the overview row).
  3. Compare. The usage panel reflects the Redis counters rnpd bills from, so the debit should follow from the same bytes.
A mismatch means one of two things: a debit has not yet posted for usage already counted (usage leads the ledger, since debits are written by rnpd after metering), or the period boundary differs between the two views. Check overview.period before concluding anything from a timestamp near a period edge. When they disagree and both are current, the ledger wins. It is the money record; the counter is the input to it.

Reading the empty and error states correctly

On a billing page, a zero is a claim. The screen is built so that a failed fetch can never be mistaken for a quiet period: If the overview is unavailable, the whole page collapses to that single state — the ledger panel is not shown alongside it, because a money page that renders half its numbers invites the wrong conclusion about the other half.