diff --git a/docs/roa-overview.md b/docs/roa-overview.md
new file mode 100644
index 0000000000..a0d049abec
--- /dev/null
+++ b/docs/roa-overview.md
@@ -0,0 +1,904 @@
+# Resource Owner Allocation (ROA)
+
+How Wire pays for CPU, NET, and RAM — and why end users don't.
+
+---
+
+## The short version
+
+**A new account on Wire costs its owner nothing and can immediately use every provisioned
+contract on the network.** It holds no CPU or NET allocation and needs none for ordinary
+contract-paid calls — because the applications it calls pay instead of it.
+
+That is the whole model in one line. The rest of this page is how it works and what it costs the
+people who do pay.
+
+**Applications pay, users don't.** When you sign an ordinary transaction, the CPU time and network
+bandwidth it consumes are billed to the *contract each declared action calls*. Sending tokens,
+swapping on a DEX, minting an NFT: the signing account is charged nothing and needs no allocation,
+stake, or rental.
+
+**Applications get paid for by node owners.** A contract cannot conjure its own capacity. It
+receives a **policy** — a grant of CPU, NET, and RAM weight — from a **node owner**, who holds a
+fixed share of the network's capacity determined by their tier and issues slices of it through the
+`sysio.roa` contract. Only registered node owners can issue policies, and only out of their own
+tier budget — so an ordinary account cannot grant itself bandwidth. (Privileged system paths
+sit outside this: `setalimits` and the fixed account gift can set limits without a policy.) A node
+owner may name itself as its own policy's recipient; registration does that for tier 1 only.
+
+**Under default billing, a contract without a policy does not run when called directly.** Because
+the contract is the payer for actions declared on it, an unprovisioned one has nothing to pay with
+and those calls fail. Provisioning the contract — not the user — is what makes an application
+usable.
+
+For those calls there is one exception to who pays, covered in
+[Who pays](#who-pays-the-payer-model): an account can volunteer to pay for itself, which takes the
+contract's own CPU and NET out of the picture. It is opt-in, it requires signatures, and it is not
+how ordinary traffic works.
+
+---
+
+## How this compares to Antelope-family chains
+
+Antelope-family chains bill the signer, and over time offered three ways to fund that signer's
+CPU and NET. Not every chain ran all three, and they arrived in sequence rather than as a set — so
+what you have used depends on which chain and which era.
+
+**Staking (2018).** Tokens locked with `delegatebw` bought a proportional share of CPU and NET, and
+RAM was bought outright from a Bancor-curve market with `buyram`. `delegatebw` takes separate
+`from` and `receiver` arguments, so a third party could stake on a user's behalf without that user
+holding any token — but somebody still had to hold and lock tokens per user, keep track of the
+`undelegatebw` timing to get them back, and understand three resource types to do it. A dApp
+onboarding new users therefore built a funding or custodial layer of its own. RAM price moved with
+the market, so contract deployment cost moved with it.
+
+**REX (2019).** A lending market let token holders rent out their staked CPU/NET for yield instead
+of leaving it idle. It improved capital efficiency and added a third system to understand alongside
+staking and the RAM market.
+
+**PowerUp (2021).** Replaced REX rentals with an expiring rental priced off a utilization curve.
+Simpler to use — you paid for a fixed term of CPU/NET — and, like `delegatebw`, it separates the
+fee `payer` from the resource `receiver`, so the user need not hold the token. The term is
+configuration (`powerup_days`, 30 in the reference contract, and each order must match it), not
+inherently daily. It is not stateless either: the system contract keeps a `powup_state` singleton
+whose utilization drives the price, plus an order table that has to be expired as rentals lapse.
+Somebody still pays per user, per term, at a price that moves with the curve.
+
+In all three, the account that signs is the account that is *billed* — funding it is a separate
+problem each of them leaves to the application.
+
+On Wire the signer is billed only if it asks to be. By default the contract is, and it is
+provisioned once by a node owner issuing a policy, after which every account that calls it
+transacts without charge. A signer that names itself with `sysio.payer` takes the bill instead —
+the exception, not the ordinary path.
+
+| | EOS staking | REX | PowerUp | **Wire ROA** |
+|---|---|---|---|---|
+| Who is billed for a transaction | Signer | Signer | Signer | **Each declared action's contract, unless that action names an explicit payer** |
+| End user needs native token | No — but someone does, per user | No — but someone does, per user | No — but someone does, per user | **No — the contract's one policy covers every caller** |
+| CPU/NET acquired by | Locking tokens | Renting from a pool | Term fee on a curve | **A node owner's policy** |
+| RAM acquired by | Bancor market purchase | same | same | **A node owner's policy** |
+| Acquisition price set by | RAM market | Rental market | Utilization curve | **Off-chain, between issuer and recipient** ([what the weight then buys is on-chain](#how-weight-becomes-throughput)) |
+| Reclaimable by | Unstake (3d) | Sell rex | Expires at term | **`reducepolicy` at or after `time_block`** |
+| Cost to onboard a user | Per-user stake + RAM, held or funded by someone | same | Per-user recurring fee | **Nothing per user — the contract is already provisioned** |
+
+---
+
+## The three resources
+
+Wire meters the same three resources Antelope does.
+
+**CPU** — execution time, in microseconds. Metered per action the transaction declares and billed to
+that action's payer; an inline action is timed inside the declared action that triggered it.
+
+**NET** — transaction size on the wire, in bytes. Billed per declared action based on its serialized
+billable size; inline actions add none.
+
+**RAM** — persistent state: account rows, permissions, contract code, and every table row a
+contract writes. Measured in bytes. Unlike CPU and NET, RAM is not a rate — it is an occupancy
+level. It is consumed when state is written and released when state is deleted, or replaced by a
+smaller value — `setcode` and `setabi` apply a signed `new_size - old_size` delta.
+
+CPU and NET replenish continuously over an averaging window. RAM does not replenish; it is freed
+only by deleting or shrinking the data holding it.
+
+---
+
+## Who pays: the payer model
+
+This is the part that differs most from Antelope, and it is worth being precise about.
+
+**The default: the called contract pays.** An ordinary transaction names no payer at all, so the
+account billed for each action the transaction declares is the contract that action invokes.
+Nothing in the transaction has to say so and no permission has to be added — this is what happens
+when you do nothing special, which is the case for essentially all user traffic.
+
+**The exception: an account can volunteer to pay for itself.** Wire reserves a permission name,
+`sysio.payer`, for this. It is not a way to bill a stranger, and adding it is not a way to obtain
+resources. On a regular action the protocol requires all three of the following together:
+
+- the `sysio.payer` entry sits at **index 0** of the action's authorizations,
+- the **same actor** also appears on that action under a real permission, and
+- the transaction carries **signatures** satisfying that paired declared authorization.
+
+A context-free action takes the opposite form. It may carry the marker *alone* — a paired real
+permission there is rejected outright — and only when that same actor is already an explicit payer
+on one of the transaction's regular actions.
+
+The paired permission is not required to be `active`. `authorization_manager` looks for any entry
+whose actor matches the payer and whose permission is not `sysio.payer` itself, then satisfies it
+like any other declared authority — so `owner`, or a custom permission linked to that action, works
+as well:
+
+```cpp
+// libraries/chain/authorization_manager.cpp — checking the explicit payer is paired
+if (auth.actor == payer && auth.permission != config::sysio_payer_name) {
+ foundPayer = true; // any real permission of the payer, not `active` specifically
+ break;
+}
+```
+
+On a declared regular action you can only volunteer yourself, or someone who co-signs, and that
+account needs its own CPU and NET allocation. Inline actions differ on both counts, below.
+
+Those two rules are the whole of `action::payer()`:
+
+```cpp
+// libraries/chain/action.cpp
+account_name action::payer() const {
+ if (!authorization.empty() && authorization[0].permission == config::sysio_payer_name)
+ return authorization[0].actor; // the exception: index 0 named a payer
+ return account; // the default: the contract being called
+}
+```
+
+```mermaid
+flowchart TD
+ A["Transaction-declared regular action"] --> B{"authorization[0].permission == sysio.payer ?"}
+ B -->|"No — ordinary traffic"| C["Payer = the contract being called"]
+ B -->|"Yes — opt-in"| D{"Same actor also present with a real permission, and signed for?"}
+ D -->|"No"| E["Rejected: unsatisfied authorization"]
+ D -->|"Yes"| F["Payer = that actor"]
+ C --> G["Contract needs a policy for actions declared on it. Signer not billed."]
+ F --> H["Actor billed CPU and NET, contract not. Marker authorizes RAM billed to the actor; contract may still bill RAM to itself."]
+```
+
+Declared regular actions only. A context-free action takes the marker alone, as above; inline and
+notification execution differ on both authorization and billing, below.
+
+The objective billing map is populated only from the actions a transaction *declares*, and is keyed
+on `payer()` — so two declared actions sharing a payer aggregate into one entry. An authorizing
+account that is not the payer never enters it, so consensus neither charges nor limits its CPU and
+NET. Nor does a contract reached only inline: its work is timed inside the declared action that
+triggered it and billed to that action's payer, so it needs no CPU or NET allocation for that work.
+A producer running [subjective billing](#subjective-billing-meters-the-signer) does meter the
+signer, but that is node-local rather than consensus.
+
+RAM is separate: it is checked against the authorizations on the action *executing*, so a callee
+that bills state to itself still needs the capacity — and an inline action carrying the marker for a
+user who has granted the contract `sysio.code` bills that user's RAM while its CPU and NET stay with
+the declared action's payer.
+
+| Action authorizations | Payer | Notes |
+|---|---|---|
+| `{alice, active}` | `sysio.token` (the contract) | The default. Alice pays nothing. |
+| `{alice, sysio.payer}, {alice, active}` | `alice` | Explicit self-pay. Declared, Alice needs her own CPU and NET allocation; inline, the marker covers RAM only. |
+| `{alice, sysio.payer}, {alice, active}, {bob, active}` | `alice` | Alice is the payer for the whole action; Bob is neither billed nor limited. |
+
+### Porting a contract from Antelope
+
+On Antelope your contract works as soon as it is deployed, because the users calling it arrive with
+their own staked or rented CPU and NET. On Wire they do not — every user account holds zero. Deploy
+a ported contract, push a transaction the way you always have, and the call fails:
+
+```
+account net usage is too high: 132 > 0
+```
+
+Nothing is wrong with the contract, the transaction, or the signer — the contract is the payer and
+has no policy. It will look dead until a node owner issues it one.
+
+The fix is not to ask users to acquire resources. It is a single `addpolicy` on the contract
+account, and for a contract that bills its own state that is the whole of it — the same unmodified
+transaction then succeeds, and every user transacts for free.
+
+**One porting change is not optional, though.** A great many Antelope contracts name an
+authorizing user as the RAM payer for rows they create — the `{user, "active"_n}` idiom, with
+`user` passed to `emplace`. Wire rejects that transaction whatever policy the contract holds:
+`validate_account_ram_deltas` requires a payer other than the receiver to carry the `sysio.payer`
+permission on the action, and `active` does not satisfy it:
+
+```
+Requested payer alice did not authorize payment. Missing sysio.payer.
+```
+
+Two ways through, and the choice is a product decision:
+
+- **Bill the rows to the contract** (`emplace(get_self(), ...)`). The contract's policy covers the
+ storage, users stay free, and the caller's transaction is unchanged. This is the gasless path,
+ and what most ports want.
+- **Keep billing the user**, which requires the client to add `sysio.payer` at index 0 — making
+ the user the CPU and NET payer too, so they need their own allocation. This one is closed to an
+ *unprivileged* notification handler: `validate_account_ram_deltas` rejects a positive delta billed
+ away from the receiver inside a notify context before it looks at the marker at all. Such a
+ handler bills its own account, or the write moves to a direct or inline action.
+
+A policy makes an unprovisioned contract callable; it does not make every Antelope contract
+portable unchanged. Check where the contract bills its RAM first.
+
+### Why the contract can afford it
+
+The executable system contracts — `sysio.token`, `sysio.msig`, `sysio.wrap` and the rest — carry
+unlimited CPU and NET, so their calls are covered by the system itself. That is established, not
+inherited: native `newaccount` writes 0/0/0 and transfers the fixed RAM gift, then `setsyscode` →
+`giftram` sets CPU and NET to `-1` while funding the code RAM. `sysio.roa` preserves it on any
+later quota sync by passing `-1, -1` for a `sysio.`-prefixed owner. The prefix alone guarantees
+nothing — `activateroa` deliberately creates
+`sysio.acct` at **zero** CPU and NET, a pure RAM bucket for account creation.
+
+> **What `-1` means here.** It is a **sentinel** meaning "no limit," not a quantity. The chain
+> tests for it explicitly (`is_unlimited_cpu` returns `cpu_weight == -1`) and substitutes a large
+> finite number where arithmetic is needed. Seeing `-1` in a `get_account` response is not an
+> overflow.
+
+A third-party contract is different. It is an ordinary account, and it is the payer for every action
+a transaction declares on it without naming one explicitly — which is every ordinary call — so it
+needs a real allocation. That is exactly what a ROA policy provides.
+
+### What happens with no policy at all
+
+An account created on Wire starts at **zero CPU, zero NET**, and 1,144 bytes of RAM — just enough
+for its own account and permission rows:
+
+```cpp
+// contracts/sysio.system/src/sysio.system.cpp — native::newaccount
+set_resource_limits( new_account_name, 0, 0, 0 );
+transfer_ram( get_self(), new_account_name, sysiosystem::newaccount_ram ); // 1144 bytes
+```
+
+For a **user** account making an ordinary contract-paid call, consensus never consults those zeros.
+It signs, the contract pays, the transaction succeeds. Opt into self-pay and they are consulted like
+any other payer's.
+
+For a **contract** account, those zeros are fatal under default billing. The contract is the payer,
+so a transaction that declares an action on it with no policy fails outright:
+
+```
+account payloadless net usage is too high: 132 > 0
+```
+
+The transaction throws. Nothing is charged *objectively* — but this failure lands after
+authorization, so the signer accrues subjective CPU and a failure count against the limiter.
+
+That holds only while the contract is the payer. Because the billing map is keyed on `payer()` and
+nothing else, an action carrying `{caller, sysio.payer}` puts the caller in the map and leaves the
+contract out of it entirely — the contract's zero CPU and NET are never consulted. A provisioned
+caller or relayer can therefore drive an otherwise unprovisioned contract, so long as the caller
+has the capacity and any RAM the contract bills to *itself* is covered.
+
+---
+
+## Policies
+
+A policy is a grant of resource weight from a node owner (the **issuer**) to an account (the
+**owner**), recorded in `sysio.roa`.
+
+| Field | Meaning |
+|---|---|
+| `net_weight` | SYS-denominated weight granted for NET |
+| `cpu_weight` | SYS-denominated weight granted for CPU |
+| `ram_weight` | SYS-denominated weight granted for RAM |
+| `bytes_per_unit` | Bytes per 0.0001 SYS, **frozen at the moment the policy was created** |
+| `time_block` | Block height before which the policy cannot be reduced or reclaimed |
+
+RAM weight converts to bytes at `bytes_per_unit`. At the network's launch price of 104 bytes per
+0.0001 SYS, **1 SYS of `ram_weight` ≈ 1.04 MB**.
+
+CPU and NET weight do not convert to a fixed quantity — they buy a proportional share of network
+throughput. See [How weight becomes throughput](#how-weight-becomes-throughput).
+
+### Who can issue one
+
+Any registered node owner, of any tier. The check is membership, not rank:
+
+```cpp
+require_auth(issuer);
+check(nodeowners.contains(node_key), "Only Node Owners can issue policies for this generation.");
+```
+
+There is no tier gate on `addpolicy`, `expandpolicy`, `extendpolicy`, or `reducepolicy`. A tier-3
+node owner has exactly the same policy powers as a tier-1 node owner. What differs is budget size.
+
+### Node owner tiers
+
+| Tier | Share of `total_sys` per owner | Max owners | Aggregate share when full |
+|---|---|---|---|
+| 1 | 4% | 21 | 84.0% |
+| 2 | 0.15% | 84 | 12.6% |
+| 3 | 0.003% | 1,000 | 3.0% |
+
+Registration consumes part of an owner's own budget — 10% of the tier allocation set aside into
+the network RAM pool, plus a flat personal policy for the owner's own account: 0.0080 SYS of RAM
+and 0.0500 SYS each of NET and CPU, 0.1080 SYS in total — **tier 1 only**. Tiers 2 and 3 receive
+no personal policy: managing policies costs an owner nothing, so they need no allocation of their
+own and keep the whole remainder issuable. Using the launch configuration of 75,496 SYS
+`total_sys`:
+
+| Tier | Total allocation | Free to issue after registration | ≈ RAM if spent entirely on RAM |
+|---|---|---|---|
+| 1 | 3,019.8400 SYS | ~2,718 SYS | ~2.8 GB |
+| 2 | 113.2440 SYS | ~102 SYS | ~106 MB |
+| 3 | 2.2649 SYS | ~2.04 SYS | ~2.1 MB |
+
+Every `addpolicy` and `expandpolicy` checks `total_new_allocation <= node.total_sys -
+node.allocated_sys`. A node owner cannot issue more than they hold.
+
+### What registration provisions
+
+`regnodeowner` spends part of the tier allocation before the owner has issued anything:
+
+| Component | Amount | Scales with tier |
+|---|---|---|
+| `sysio` RAM pool grant | 10% of the tier allocation | Yes — every tier |
+| Personal RAM | 0.0080 SYS (8,320 bytes) | Tier 1 only |
+| Personal NET | 0.0500 SYS | Tier 1 only |
+| Personal CPU | 0.0500 SYS | Tier 1 only |
+
+The 10% grant is not for the owner. It moves bytes into `sysio`'s RAM pool, which funds the
+1,144-byte gift every new account on the network receives. It is written with
+`time_block = UINT32_MAX` and is never reclaimable.
+
+A tier-1 owner's three personal components land in a self-issued policy — `issuer == owner` —
+carrying `time_block = 1`, so it can reshape or reclaim them immediately with `expandpolicy` or
+`reducepolicy`. Tier 1 gets them because it is the only tier that can call `newuser`, whose
+`sponsors` and `sponsorcount` rows are the only writes in the contract billed to a node owner. The
+10% grant is not tier-gated.
+
+### What a node owner needs to operate
+
+Almost nothing. Managing policies costs a node owner no resources at all:
+
+- `addpolicy`, `expandpolicy`, `extendpolicy`, and `reducepolicy` are actions on `sysio.roa`, so
+ `payer()` resolves to `sysio.roa`, which carries unlimited CPU and NET.
+- Every row the contract writes for registration and policy management is billed to `sysio.roa`
+ as well — `policies`, `reslimit`, and `nodeowners` rows are all `emplace(get_self(), …)`.
+
+An owner reduced to zero CPU, zero NET, and zero spare RAM can still issue a policy. Membership —
+the `nodeowners` row — is what confers the ability to issue, not any allocation the owner holds.
+
+The one exception is tier-1's `newuser`, which bills its `sponsors` and `sponsorcount` rows to
+`creator`. Those are the only two writes in the contract charged to a node owner, so a tier-1
+owner needs RAM headroom before its first `newuser` call.
+
+### What differs between tiers
+
+| | Tier 1 | Tier 2 | Tier 3 |
+|---|---|---|---|
+| Issue, expand, extend, reduce policies | Yes | Yes | Yes |
+| Budget per owner | 3,019.8400 SYS | 113.2440 SYS | 2.2649 SYS |
+| Max owners | 21 | 84 | 1,000 |
+| `newuser` (sponsored accounts) | Yes | No | No |
+
+Policy mechanics are identical across tiers — there is no tier check in any of the four policy
+actions. `newuser` is the only tier-gated capability, guarded by
+`check(node.tier == 1, "Creator is not a registered tier-1 node owner")`.
+
+### Where network RAM comes from
+
+`activateroa` splits the SYS left over after all tier allocations between two pools:
+
+| Pool | Size | Funds |
+|---|---|---|
+| `sysio.roa` | ~157 MB at activation | The contract's own rows: policies, reslimits, node-owner records |
+| `sysio` | ~157 MB at activation, ~7.98 GB once every node owner has registered | The 1,144-byte gift every new account receives |
+
+`sysio`'s pool grows as owners register, because each registration deposits 10% of its tier
+allocation into it. At 1,144 bytes per account, the funded pool supports roughly 6.97 million
+accounts.
+
+The gap between those two pools is why `newuser` bills its sponsorship rows to the sponsoring
+tier-1 owner rather than to the contract. Billed to the contract, `sysio.roa`'s ~157 MB would cap
+sponsorship near **1.09 million** users — well short of what `sysio`'s account pool can create. A
+tier-1 owner's own free budget of ~2.83 GB is the only one that scales with how many users it
+actually onboards.
+
+A sponsorship costs **144 bytes** — a 16-byte key and 16-byte value over
+`billable_size_v` of 112 — plus a one-time 144-byte `sponsorcount` row on a creator's
+first `newuser`. Neither pool is a ceiling: `addpolicy` refuses CPU and NET to a `sysio.`-prefixed
+account but not RAM, so a node owner can extend either with a RAM-only policy. The ~157 MB is
+where `activateroa` starts them.
+
+### The four policy actions
+
+**`addpolicy`** — create a policy. Fails if this issuer already has one for this owner (use
+`expandpolicy`). Weights must be in the core SYS symbol, non-negative, and at least one non-zero.
+
+**`expandpolicy`** — add weight to an existing policy. Converts RAM at the *policy's* frozen
+`bytes_per_unit`, not the current network price.
+
+**`extendpolicy`** — push `time_block` further out. It can only move forward, never back, and never
+to a block already in the past. A policy's term can be lengthened but not shortened.
+
+**`reducepolicy`** — take weight back. Callable at or after `time_block`. Each weight is
+capped at the stored policy weight, so an issuer can never withdraw more than they granted.
+
+### Stacking policies from multiple issuers
+
+The policy table is scoped per issuer, so the duplicate check is per issuer only. **An account can
+hold policies from many node owners at once, and they sum into a single quota.**
+
+Two node owners each granting `10.0000 SYS` of CPU and NET to the same account produce a combined
+on-chain limit of `200000` weight units. Reduce one issuer's policy fully and the account drops to
+`100000` — the other issuer's grant is untouched, and they can still expand it independently:
+
+```
+addpolicy owner_A -> acct 10.0000 SYS cpu/net
+addpolicy owner_B -> acct 10.0000 SYS cpu/net
+ net = 200000 cpu = 200000
+
+reducepolicy owner_A (full) net = 100000 cpu = 100000 # B untouched
+expandpolicy owner_B +5 SYS net = 150000 cpu = 150000
+```
+
+This is how a contract gets provisioned by more than one sponsor: several node owners co-sponsor
+the account, each on their own terms, each able to reduce its own policy without touching another
+issuer's row.
+
+Four things to know when stacking:
+
+- **Each policy has its own `time_block`.** The lock is per issuer. An account's total capacity is
+ only as stable as its shortest-committed policy.
+- **Each policy freezes its own byte price.** After a network-wide `setbyteprice`, two policies with
+ identical `ram_weight` can represent different byte counts.
+- **RAM reclaim competes, and an exit is not guaranteed.** CPU and NET always unwind in full —
+ they are rate limits with nothing to hand back. RAM can be *occupied*: `reducepolicy` reclaims
+ `min(unused RAM on the whole account, requested)`, floored to a whole `bytes_per_unit`, and
+ decrements the policy row and the issuer's `allocated_sys` by what was **reclaimed**, not
+ requested. Grant spent on live state therefore stays counted against the issuer's budget until
+ that state is deleted or shrunk, and the row is erased only once all three weights reach zero. Because the
+ cap is the *account's* unused RAM rather than this policy's share, one issuer's grant can be
+ pinned by state another issuer's grant paid for.
+- **The ceiling is network-wide unallocated SYS,** not a per-account cap. There is no limit on how
+ many policies one account may hold.
+
+---
+
+## How weight becomes throughput
+
+CPU and NET use the same proportional-share formula Antelope uses. An account's share of the
+network's virtual capacity over the averaging window is:
+
+```
+max_use_in_window = virtual_capacity_in_window × your_weight / total_weight_across_all_accounts
+```
+
+The denominator is `resource_limits_state`'s `total_cpu_weight` (or `total_net_weight`): the sum
+of every account's positive weight, whatever set it. Accounts with unlimited (`-1`) limits
+contribute zero. In normal production every positive weight comes from a ROA policy, so the two
+are the same number — but privileged `setalimits` / `setacctcpu` / `setacctnet` can add weight
+outside ROA, and bootstrap does.
+
+The mechanism is unchanged from Antelope. **Only the source of the numerator changed.** In
+Antelope the weight came from tokens staked with `delegatebw`. On Wire it comes from a policy a
+node owner issued. Nothing about an account's token balance affects its resource share.
+
+The practical consequence is the same as on Antelope: throughput is a share of a moving total. If
+the network's total allocated weight grows and an account's does not, its slice shrinks. The only
+remedy is more weight — `expandpolicy` from the existing issuer, or a policy from an additional
+node owner.
+
+`setbyteprice` does not help here. It rewrites `roastate.bytes_per_unit`, which governs how RAM
+weight converts to bytes for policies struck afterwards, and touches neither CPU/NET weights nor
+the total that divides them. That is also why each policy records the price it was struck at: so
+its RAM conversion stays fixed when the network price later moves.
+
+---
+
+## Spam control
+
+If the contract pays, what stops someone calling it in a loop for free? Two things, at two
+different layers.
+
+### The contract's own share is a hard cap
+
+CPU and NET are rate limits over a window, and the payer's limit is the one that applies. Spam
+aimed at a contract consumes that contract's own account quota, not another account's rolling
+share — though every success it lands also draws on block-wide capacity. Once exhausted, further
+calls fail with `tx_cpu_usage_exceeded` or `tx_net_usage_exceeded` naming the contract, and keep
+failing until the window rolls forward.
+
+That is the structural answer, and it bounds the *quota* damage: the spammer draws on the payer
+account's own provisioned share — the sum of every policy stacked on it — never another account's.
+Each issuer's exposure stays bounded by its own grant, reclaimable via `reducepolicy` at or after
+`time_block`.
+
+**It does not make the blast radius zero.** Every transaction that succeeds is billed twice over —
+once to the payer's rolling window, and once to the block:
+
+```cpp
+// libraries/chain/resource_limits.cpp — add_transaction_usage
+// account for this transaction in the block and do not exceed those limits either
+rls.pending_cpu_usage += total_cpu_usage;
+rls.pending_net_usage += total_net_usage;
+```
+
+`pending_*_usage` is what `get_block_cpu_limit()` / `get_block_net_limit()` subtract from to size
+the *next* transaction's limits, so a heavily provisioned contract being hammered inside its own
+quota still occupies block capacity and can delay unrelated traffic. What the quota guarantees is
+that the spam stops when that contract's share runs out — not that nobody else notices while it
+lasts. Bounding the shared-capacity effect is the second layer's job.
+
+This is objective, part of consensus, and applies on every node with no configuration.
+
+### Subjective billing meters the signer
+
+The objective rules never look at the signer. `nodeop` can, and that is where a producer
+distinguishes a spammer from a legitimate user of the same contract.
+
+Before executing, `transaction_context::verify_init_subjective_billing` takes the first
+authorizers that are **not** payers and checks each one:
+
+```
+available = subjective-account-cpu-allowed-us
+ + the account's own objective CPU limit
+ − its accumulated subjective bill
+```
+
+For an ordinary account with no policy the objective limit is `0`, so the budget is exactly
+`subjective-account-cpu-allowed-us` — **300,000 µs (300 ms) by default**. When `available` reaches
+zero the transaction is dropped before execution, with a message of the form:
+
+```
+Subjectively terminated trx . Authorized account exceeded
+subjective CPU limit 300000us by us with an objective cpu limit of 0us.
+```
+
+`update_billed_cpu_time` feeds it: when an action's first authorizer differs from its payer, the
+CPU is recorded against the signer as well, so the signer accrues a running cost even though the
+objective bill went to the contract.
+
+Two properties keep this landing on abusers rather than on ordinary users:
+
+- **Successful transactions do not accumulate.** A subjective bill is held as `pending_cpu_us` and
+ removed when the transaction appears in a block. Traffic that lands costs the signer nothing over
+ time.
+- **Most failures accumulate and decay slowly.** A failed transaction moves its bill into a
+ decaying accumulator whose window is `subjective-account-decay-time-minutes` — 24 hours by
+ default. Repeatedly submitting transactions that fail is what burns the budget.
+
+ Two classes are deliberately excluded, so a signer is not punished for conditions it did not
+ cause: **duplicates** (`tx_duplicate`), and **block-level exhaustion** —
+ `block_cpu_usage_exceeded`, `block_net_usage_exceeded`, `deadline_exception`,
+ `interrupt_exception`, and the read-only VM-OC compile failure. Whether anything accumulates at
+ all depends on how far the transaction got — see below.
+
+A blunter limiter runs alongside it: `subjective-account-max-failures` (default `3`) per
+`subjective-account-max-failures-window-size` blocks (default `1`). An account over the limit has
+its transactions rejected outright until the window resets:
+
+```
+transaction exceeded failure limit for account until