Capability limits
Does this action stay inside your tier’s ceilings? App slots, bandwidth, custom domains and storage capacity are each enforced where they are used (see Capability gates below).
A tier is only as good as its enforcement. Smart Engines reads your subscription on the way in (can you do this at all, and are you within quota?) and meters the work on the way out (debit what you used). This page is the runtime side of the tiers.
Gating answers two questions from your tier:
Capability limits
Does this action stay inside your tier’s ceilings? App slots, bandwidth, custom domains and storage capacity are each enforced where they are used (see Capability gates below).
Quota
Are you within your tier’s per-day allowance? The daily limits are derived from the tier’s per-month allowances: the monthly figure divided by 30.
A tier also declares features flags and allowedNetworks. No request is gated on either today: every tier lists mainnet, testnet and previewnet, and the feature flags describe the tier rather than switch anything on or off.
The tier itself is resolved per request from the cluster’s app registry, which defaults unknown apps to builder — the lowest tier, the most conservative quota — so unattributed traffic can never be unbounded.
flowchart TD
Req["Incoming request (appId)"] --> Tier["Resolve tier<br/>(app registry)"]
Tier --> Cap{"Inside the tier's<br/>capability ceilings?"}
Cap -->|no| Deny["Reject (403 / 429 / 507)"]
Cap -->|yes| Quota{"Within derived daily limit?"}
Quota -->|over limit| Block["429 — quota exceeded<br/>(grace period may apply)"]
Quota -->|within| Serve["Serve request"]
Serve --> Meter["Meter usage + debit balance"]
The pre-flight gate refuses work that’s outside your tier before doing it. The metering side records and debits after the work succeeds.
The tier catalog doesn’t only describe your allowances — specific per-tier ceilings are enforced at the point of use across the host and gateway, and every one reads its limit from the tier catalog (never a hard-coded number), so re-tuning the catalog re-tunes the gate. Four of these gates run today:
| Gate | Where it runs | Catalog source | On breach |
|---|---|---|---|
| App-slot quota — a net-new app beyond your tier’s app allowance is refused (the app you’re re-deploying doesn’t count against itself) | Host deployment path | host.appSlots |
403 forbidden |
| Bandwidth ceiling — once you’ve served your tier’s bandwidth for the period, further requests are cut off | Host, on an app’s routes | gateway.bandwidthGb |
429 too many requests |
| Custom-domain gate — a tier granting zero domains may attach none, and no tenant may exceed their domain count | Gateway domain registration | gateway.domains |
403 forbidden |
| Storage capacity — an upload that would take the app past its tier’s storage allowance is refused | Host storage upload | host.storageGb |
507 insufficient storage |
Enterprise allowances are unlimited, so those tiers never trip a ceiling. Each gate resolves the tier at request time and is written to fail safe for its context: when the tier can’t be resolved, the app-slot gate goes inert and the storage gate falls back to the flat default quota — abuse/cost controls, not the hard ownership boundary — while the custom-domain gate defaults a genuinely-unknown owner to the most conservative tier. The custom-domain gate resolves tier from the owner wallet (the subscription’s developerAccountId), not the appId, so a paid tenant isn’t silently clamped to the entry tier.
Daily quota is enforced against the per-day allowance derived from the tier’s per-month allowances. The validator exposes a usage status surface (GET /api/v3/subscription/usage/status/:appId) that reports, per app:
usagePercent and currentUsage against dailyLimit,inGracePeriod / graceExpiresAt) before hard blocking,blocked,suggestUpgrade hint plus the concrete nextTier (its name, its derived daily limit, and monthlyPriceUsd),projectedDaysUntilLimit estimate from your current burn rate.So hitting your ceiling isn’t a silent wall — the platform tells you you’re close, offers a grace window, and names the tier that would fix it.
Every subscription is deposit-backed — there is no free tier. The confirmed deposit is a prepaid float: the HSUITE stays in the deposit account, and the subscription’s fundedAmount and remainingBalance are seeded from it. As work is done, per-op usage accrues and decrements remainingBalance, and each deduction is recorded to a charge ledger you can read back per app. A subscription is valid when it is ACTIVE and not past its expiresAt — those two must hold. A positive balance is not part of that check: an empty float blocks only overage charging (below), never per-request admission.
The monthly tier fee itself is the credit that funds the float — it is not an extra charge and never accrues. Only per-op usage accrues. Accrued usage is settled out of the deposit account in batched 40/40/20 sweeps (burn / runners / treasury), not all at once: a sweep fires when the accrued amount crosses a threshold or on a periodic backstop. The float stays in the account between sweeps, so remainingBalance = fundedAmount − settledAmount.
Usage only draws the float down, so a busy pay-per-use app can drive remainingBalance to zero — at which point overage charging stops (per-request admission does not gate on balance, and neither does the deployer scale-down gate below). It’s recoverable without changing tier or waiting for renewal: hsuite topup re-funds the float in place, adding spendable balance while leaving the deposit bond untouched. See Same-tier top-up.
Every metered host and gateway call computes an HSUITE cost for the operation — the amount the op draws from the float — and records it. The USD→HSUITE conversion behind that cost uses the platform’s single canonical HSUITE/USD rate: the host and gateway fetch the validator’s own rate (derived from the XRPL on-ledger market) rather than a third-party price feed, so per-op pricing agrees with the rate that priced your deposit (see One rate for the whole platform). What happens to that cost depends on whether live charging is on:
| Charging | What happens | Net effect |
|---|---|---|
| On (the default) | The cost is computed and deducted from the app’s float, exactly once, each time the accrued total crosses a whole unit | Usage is metered and deducted |
| Off (an operator choice) | The cost is computed and recorded; nothing is deducted | Usage is metered, nobody is charged |
The validator only deducts a whole smallest-unit, but most ops cost a fraction of one HSUITE. Rounding every op up independently would charge a 0.012 HSUITE call a full 1 HSUITE — many times the published price on the cheapest, highest-volume operations.
So each op’s exact decimal cost is folded into a per-app running remainder, and an integer charge is made only once that remainder crosses a whole smallest-unit — the crossed whole units are taken by floor, never invented by ceil. Over any sequence of ops the arithmetic is exact:
Σ charged + finalRemainder == Σ costso nothing is fabricated and nothing is dropped. The exactly-once guarantee is unchanged (the charge ledger still dedups on the op’s idempotency key); accrual only governs how a stream of decimal costs becomes whole-unit charges. The per-app remainder is stored durably and shared by every replica serving the app, so a restart neither drops nor double-counts it.
Distinct from per-request gating, the deployer runs a background gate: an app whose subscription reads inactive can be scaled to zero replicas so a dead account doesn’t keep consuming cluster capacity. Three properties make this safe rather than a footgun.
It does not gate on balance. A drained-but-ACTIVE, non-expired subscription is not scaled down — only a genuinely invalid one (not active, or past expiresAt) is. A busy pay-per-use app that simply spent its float keeps running.
The scale-down is quorum-gated across clusters. A single cluster’s local “inactive” verdict is not enough to scale an app to zero. Before acting, the deciding cluster polls its peers and requires an origin-verified majority of clusters to agree the subscription is inactive; a lone stale or divergent cluster cannot unilaterally stop a healthy tenant.
And the scale-down is reversible. When the subscription reads valid again — for example after a renewal — the deployer scales the app back up on its next reconcile, comparing the live replica count against the desired count. Scale-down and scale-up are the same authority acting symmetrically.
Gating and quota are the access half; the metering and consumption machinery on the host side is what actually counts usage by category (database, functions, messaging, storage) and feeds the numbers the quota check reads. The two are designed as one loop: meter what happened, gate the next request on the result.