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.
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 ofoverview.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.
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 inledger.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 kindtopup. 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 newtopup 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:- Take an account’s
egressBytesfrom the billable-egress panel. - Find that account’s
egressentries in the ledger for the same period (match on the shortenedsub, then confirm with the fullsubfrom the overview row). - Compare. The usage panel reflects the Redis counters
rnpdbills from, so the debit should follow from the same bytes.
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.

