X-Billing-Account-Id joins the identity set Call forwards, and Caller.Account
carries it: headers() writes it and CallerOf reads it, the same as every other
identity field. A callee that resolves who pays now reads the account the
gateway minted, over a call as over REST. A credential with no claim sends no
header.
Co-authored-by: Hanzo Dev <[email protected]>
App, Group, Operation. A group is a prefix and what holds under it, an operation
is one typed route declared there, and the verb methods are generic methods —
which Go 1.27 allows on a concrete type and still refuses on an interface. So In
and Out come from the handler and the caller writes no type arguments:
users := app.Group("/v1/users")
users.Post("/", api.Create)
mint := users.Group("/mint").With(middleware.Mint(gate))
mint.Post("/deposit", api.Deposit)
A Group is a CLOSED HANDLE: two unexported fields, and no method that hands back
the app, the parent or the fiber underneath. A subsystem given the group at its
own prefix cannot reach above it, so confinement is a property of the type
instead of a rule something has to police — which is what a host needs in order
to hand out a bounded surface and know what comes back.
That is bought by giving up the router interface, deliberately. Router and
OpTarget are gone, and with them the ability to implement a route table outside
this package: a decorator is no longer a router, a gate is middleware on a group,
and a bound is a path. The five package-level verb functions go the same way —
a registration is a method on the thing it registers into.
Raw carries what is not a typed operation: a stream, an upgrade, a probe, a
foreign subtree. It names its method, and it is the longer spelling because
opting out of the typed model is the exception and should read like one.
Co-authored-by: Hanzo Dev <[email protected]>
identityHeaders forwarded five of the nine headers a gateway asserts, so a
callee reached over the op-call plane saw a principal with no project, no
minted name, no owner and no org-admin bit — while the same handler reached
over REST saw all four. Two of them decide authority: owner carries membership
of a reserved platform org, and isOrgAdmin says only that someone administers
their own org. A subset let a callee read the second for the first, or find
neither, without anything on either side saying so.
Add WithCaller for the caller that has no inbound request to propagate. A
grant issued when an org opens and a meter that debits after the response has
gone out both act FOR a tenant; without a way to say which, the only place left
for the org is the argument, and an org in the argument is one the caller chose.
An inbound request still wins, so this supplies an identity where there is none
and cannot override one. CallerOf reads it back off the same context, so a job
sees what it is about to send.
Ctx gains Project, UserName, UserOwner and IsOrgAdmin, so the REST reader and
the call reader spell one vocabulary.
TestForwardedIdentityCrossesWhole fails on exactly the four dropped fields if
the array is narrowed again; the other three cover the stated path, the two
admin scopes staying apart, and an unattributed call staying anonymous.
Co-authored-by: Hanzo Dev <[email protected]>