Credential carried a Machine flag, derived by each caller from User.Type, and Payer read it one step above the rule that governs the signup org. So a machine-typed row took Org(SignupOrg) — the platform's own balance — without ever reaching that rule. Outside the signup org the flag decided nothing: machine and person both pool. Its only effect was to turn "is a program", a profile column, into "may spend the platform's money", an authority. The fallback exists to answer for credentials that carry no claim, and a credential carrying no claim carries no evidence of authority either — it must resolve, not grant. So the field is gone rather than merely unread: the defect is now unexpressible at the type level, and every caller that asserted a class is a compile error rather than a silent wrong answer. Machine-ness still answers the questions a column can answer (IsMachine — which column a usage record fills, whether there is a person behind the credential); who pays is stated by IAM at the identity boundary and carried in the signed billing_account claim. First-party service credentials are untouched: both doors state that claim, and the API-key door does not transmit User.Type at all. Co-authored-by: Hanzo Dev <[email protected]>
account
The billing-account rule. One value, one decision, no dependencies.
account.Payer(account.Credential{Owner: org, Name: user, Account: claim}).Subject()
(Org, Subject) is the money's address — Org names the ledger that holds
it, Subject the key within that ledger. A deposit credits that address, the
spend-gate reads it, a usage debit spends it. One address, one answer, every
caller.
Payer is the whole point: it, and only it, decides whether a request pays from
an org pool or a person's own account. That decision shipped wrong twice
by being made in two places — a gate keyed on the pool while the debit spent a
person's wallet, so a funded pool green-lit a request that drained an empty
personal one, and an empty pool 402'd a funded person. Any new money path
resolves its address here rather than re-deriving it.
Why it is its own module
It has zero dependencies — go.mod is two lines and the code imports only
strings. That is what lets every layer import it: cloud's trust boundary
(clients/principal), its metering middleware, and commerce's billing resolver
all call the same rule without any of them depending on each other.
Folding it into the commerce service would invert that: identity code would have to import a ledger, a SQLite store and an HTTP surface to answer a pure question about who pays.
Layout
account.go—Account,Org/Person/Project,Parse,Payer,Credentialorg.go— org-level helpers
Licensed under MIT OR Apache-2.0, per HIP-0137.