Documentation
¶
Overview ¶
Package core is the shared kernel of the admin subsystem: the resolved upstream clients (State) plus the one-copy business primitives every admin domain composes — the two-tier gate, the /v1 envelope writers, the tenant-scope predicate, the IAM fan-in, the single credit-grant path, the tamper-evident audit emit, and the fleet activity/time-series model. Each primitive lives EXACTLY once here; the domain packages (audit/customer/revenue/finance) and the top-level admin Mount import it, never duplicating a helper — there is one path to grant, one read, one scope rule.
Index ¶
- Constants
- Variables
- func Admit(ctx context.Context) (*zip.Ctx, error)
- func AdmitScoped(ctx context.Context, s *cloud.Service[State]) (*zip.Ctx, error)
- func BillingEventsReady(ctx context.Context) bool
- func BucketKeyOf(t time.Time, interval string) string
- func CHFirstRow(rows []map[string]any) map[string]any
- func CHInt64(v any) int64
- func CHStr(v any) string
- func CHTableExists(ctx context.Context, qualified string) bool
- func CHTime(v any) string
- func CHTimeLit(t time.Time) string
- func CallerCreds(c *zip.Ctx) iam.Creds
- func DayKey(t time.Time) string
- func Descendants(s *cloud.Service[State], org string) []string
- func Display(displayName, fallback string) string
- func EmitAudit(s *cloud.Service[State], c *zip.Ctx, action, resType, resID string, ...)
- func EnumerateBuckets(since, now time.Time, interval string) []string
- func FindOrg(s *cloud.Service[State], ctx context.Context, cr iam.Creds, org string) (*iam.Org, error)
- func IndexOf(buckets []string) map[string]int
- func ListOrgs(s *cloud.Service[State], ctx context.Context, cr iam.Creds) ([]iam.Org, error)
- func MonthKey(t time.Time) string
- func OrgMoney(s *cloud.Service[State], ctx context.Context, org string) (spend, credits int64, ok bool)
- func ParseTxnTime(s string) (time.Time, error)
- func SQLInList(vals []string) string
- func ScopedOrgs(s *cloud.Service[State], ctx context.Context, c *zip.Ctx, cr iam.Creds) ([]iam.Org, error)
- func Total(n int) *int
- func WarehouseSince(rangeLabel string) time.Time
- func WeekKey(t time.Time) string
- type CreditRequest
- type CustActivity
- type GrantOut
- type GrantResult
- type None
- type SeriesPoint
- type SourceStatus
- type State
- type TenantScope
- type TxnPoint
Constants ¶
const ( OK = "ok" Err = "error" )
The two states of the /v1 envelope every admin op answers with ({ status, msg, data, data2 } — the operator transport's get<T>/getList<T> shape). The transport surfaces anything that is not OK as an error, never a value, so a failed read is a 200 carrying Err — NOT an HTTP error status.
const ( EvSubscriptionCreated = "subscription_created" EvSubscriptionRenewed = "subscription_renewed" EvSubscriptionPlanChanged = "subscription_plan_changed" EvSubscriptionCanceled = "subscription_canceled" EvInvoiceFinalized = "invoice_finalized" EvInvoicePaid = "invoice_paid" EvInvoiceVoid = "invoice_void" EvAPIUsageDebit = "api_usage_debit" )
Canonical customer-activity event names — the CONTRACT with the commerce emitters (events/client.go). These are server-side constants (never user input), so rendering them into an IN (...) list is injection-safe.
const BillingEventsTable = "commerce.events"
BillingEventsTable is the collector-owned warehouse table the commerce customer-activity emitters land in (events/client.go → analytics-collector → commerce.events). admin only READS it (never creates it — the collector owns its writes), exactly as o11y reads hanzo.cloud_usage.
const MaxCustomerConcurrency = 8
MaxCustomerConcurrency bounds the per-org enrichment fan-out so a large fleet does not open one upstream connection per org at once. Admin is low-QPS; 8 keeps latency low without hammering IAM/commerce.
Variables ¶
var ( SubscriptionEvents = []string{EvSubscriptionCreated, EvSubscriptionRenewed, EvSubscriptionPlanChanged, EvSubscriptionCanceled} InvoiceEvents = []string{EvInvoiceFinalized, EvInvoicePaid, EvInvoiceVoid} )
SubscriptionEvents / InvoiceEvents are the lifecycle sets each fleet view folds over (latest-event-wins per entity). Closed server-side constants.
var ErrPartialRevenue = errors.New("partial: one or more org revenue reads failed")
ErrPartialRevenue marks a revenue read that succeeded at the org-list level but had one or more per-org failures — the fleet total is real but PARTIAL. SrcOf reports it as a not-ok source so the console shows a degraded state rather than presenting an under-count as authoritative.
Functions ¶
func Admit ¶
Admit is the SuperAdmin gate, called once at the top of every PLATFORM op. It is the same fail-closed predicate the old Guard wrapper applied: a request whose validated identity is not a SuperAdmin (X-User-IsAdmin != "true", which SanitizeIdentity sets only for owner == AdminOrg) is refused 403 before any upstream is touched.
It returns the request because an admitted op almost always needs it — to replay the caller's credential to IAM, or to read the body a passthrough forwards verbatim.
func AdmitScoped ¶
AdmitScoped is the gate for the ORG-SCOPED panels, called once at the top of each. It admits a SuperAdmin (principal.IsSuperAdmin) OR an admin of an ENABLED WHITE-LABEL TENANT org — three facts, ALL required for that second tier:
- X-User-IsOrgAdmin (principal.IsOrgAdmin — "admin of my own org", unforgeable because SanitizeIdentity strips it on ingress and re-mints it only from a validated isAdmin claim), AND
- a validated principal pinned to its own org (principal.Org: validated X-User-Id + non-empty in-bounds X-Org-Id, never client-chosen), AND
- that org is an ENABLED WL tenant (State.IsWhiteLabelTenant — the fail-closed allowlist; empty/unset ⇒ SuperAdmins only).
Passing this gate is not the end of the scoping: the handler then folds every read through ResolveScope/ScopedOrgs, which hard-limits a non-super caller to their own org subtree whatever the request says.
func BillingEventsReady ¶
BillingEventsReady reports whether the warehouse is connected AND the collector's commerce.events table is provisioned — the two-part gate every billing fleet view opens with, so an unwired collector degrades to an honest empty aggregate rather than an error.
func CHFirstRow ¶
CHFirstRow returns the first row or an empty map (never nil), so a parser reads honest zeros from an empty result instead of panicking.
func CHTableExists ¶
CHTableExists probes the datastore for a table's presence. The name is a package constant (never user input), so EXISTS TABLE is safe. Any error → false (honest "not available yet"), mirroring compute.computeTableExists.
func CHTimeLit ¶
CHTimeLit formats a time as a datastore DateTime literal (UTC), bound as a POSITIONAL string arg (never interpolated).
func CallerCreds ¶
CallerCreds captures the caller's replayed authorization context for the IAM fan-out: the raw Cookie header (session model) and the Authorization bearer.
func Descendants ¶
Descendants returns org + every sub-org it owns — the subtree the caller administers. See the RECURSION SEAM note above: today the singleton {org}; the ONE place a future IAM parent-org index is walked.
func EmitAudit ¶
func EmitAudit(s *cloud.Service[State], c *zip.Ctx, action, resType, resID string, before, after any, outcome audit.Outcome)
EmitAudit writes ONE compliance record for a management action to cloud's tamper-evident trail: who (the validated SuperAdmin from the sanitized identity — the gate already proved it), what (action + resource), the redacted before/after, and the outcome. This is the "before/after on a config-affecting change" the request-level middleware record cannot carry (it never reads bodies). Best-effort: a failure here is logged loud, never silent, and never double-fails the response. A nil store (unconfigured deployment) is a no-op, like the middleware.
func EnumerateBuckets ¶
EnumerateBuckets lists every bucket key from since..now inclusive so a series has a continuous axis (a zero-usage bucket is an honest 0, not a gap).
func FindOrg ¶
func FindOrg(s *cloud.Service[State], ctx context.Context, cr iam.Creds, org string) (*iam.Org, error)
FindOrg returns the IAM org by slug (nil, nil when it does not exist) so a management action can validate its target before acting — never credit or suspend an org that isn't real.
func ListOrgs ¶
ListOrgs reads the org directory (owner = admin org) as the typed shape the overview/orgs/usage/customer/revenue/finance aggregators fold over.
func OrgMoney ¶
func OrgMoney(s *cloud.Service[State], ctx context.Context, org string) (spend, credits int64, ok bool)
OrgMoney returns (spendCents, creditsCents, ok) for one org — the ONE per-org money read every fleet aggregator (overview, orgs, revenue) folds over. ok is false ONLY when a read FAILED, so a caller folds the per-org failure into a PARTIAL/degraded source rather than presenting the resulting undercount as authoritative. An org that simply has no money yet reads a clean (0, 0, true), never a failure.
CO-RESIDENCE (the money-plane trap). Commerce's own /v1/billing/* routes are behind `//go:build cloud` and are NOT compiled into this binary, so the admin commerce client's S2S reads self-dispatch by PATH into cloud's OWN handlers: GET /v1/billing/balance re-enters the customer balance handler with no principal (401) and GET /v1/billing/usage-rollup is unrouted (404). Reading commerce over HTTP would therefore fail for EVERY org and falsely mark the money source DOWN while real money sits in the co-resident ledger. So — exactly as clients/billing.balance()/usage() and core.grantDeposit already resolve it — prefer the co-resident finance ledger (finance.Current()); the commerce S2S read stays only as the split-deploy fallback.
func ParseTxnTime ¶
ParseTxnTime accepts the commerce ledger's RFC3339 forms.
func SQLInList ¶
SQLInList renders a set of server-side-constant strings as a datastore string list ('a','b',…) for an IN (...) clause. ONLY for closed constant sets (the event-name enums above) — never for user input; positional args carry all caller-derived values.
func ScopedOrgs ¶
func ScopedOrgs(s *cloud.Service[State], ctx context.Context, c *zip.Ctx, cr iam.Creds) ([]iam.Org, error)
ScopedOrgs is the ONE fan-in the org-scoped read panels (overview, orgs, usage, analytics) fold over — enforcing the two-scope predicate in a single place. A SuperAdmin gets EVERY org (the cross-tenant list); any other caller gets ONLY their own subtree, each row read from IAM so the display name / createdTime are the REAL values. An org row that can't be read best-effort degrades to a name-only row rather than failing the panel — the scope is unaffected.
func Total ¶
Total is the row count of a LIST read, as the pointer the envelope's optional data2 field takes. Present — even at zero — on a success; left nil on a failure, because a failed read has no count and adding the key would change the wire.
func WarehouseSince ¶
WarehouseSince maps the ?range enum (24h|7d|30d, default 30d) to a lower time bound, mirroring compute.computeSince so the fleet views share ONE window grammar.
Types ¶
type CreditRequest ¶
type CreditRequest struct {
AmountCents int64 `json:"amountCents"`
Currency string `json:"currency"`
Reason string `json:"reason"`
// User names the MEMBER to credit, by IAM username — the `name` half of
// "<org>/<name>". Empty credits the org itself.
//
// It exists because "the org" is not always the account a request spends from.
// In the shared signup org, whose members are strangers to each other, each pays
// from their OWN wallet; a grant keyed on the org alone lands in a pool that
// member can neither spend nor see, while they are refused at $0. Which of the
// two a grant lands on is NOT decided here — principal.WalletFor asks account.Payer,
// the same rule the spend gate asks — so a pooled tenant org keeps one balance no
// matter what is named, and a per-member org can finally be funded per member.
User string `json:"user"`
// Source splits the grant into the commerce ledger's two money buckets:
// - "trial" (default) — a non-cash promo/comp credit: spendable on non-premium
// metered usage only, NEVER refundable cash and NEVER paid out.
// - "prepaid" — real money added to the customer's cash balance. Refundable,
// GPU-eligible.
// Unknown/empty → trial (fail-closed to non-cash).
Source string `json:"source"`
}
CreditRequest is the grant body. AmountCents is the credit to add (positive only — a grant, never a silent debit). Reason is the operator's justification, recorded in the audit trail's before/after (refund / comp / support).
type CustActivity ¶
type CustActivity struct {
Org string
Display string
Created time.Time
HasCreated bool
Usage []TxnPoint
SpendCents int64
}
CustActivity is one customer's real analytics input: when they signed up (IAM createdTime) and their consumption events (commerce withdraws). Deposits are NOT activity (a credit grant is not the customer using the product), so only withdraws feed active/retention/churn/usage — the honest "used it" signal.
func FleetActivity ¶
func FleetActivity(s *cloud.Service[State], ctx context.Context, orgs []iam.Org) ([]CustActivity, bool)
FleetActivity reads every org's signup time (already on the org row) + usage ledger, folded into the pure activity model. Returns (acts, ok) where ok is false if ANY org's ledger read failed (the caller marks the source degraded and flags the ledger-backed metrics as not-fully-computed). Fanned out concurrently with a bound.
func (CustActivity) ActiveIn ¶
func (ca CustActivity) ActiveIn(bucket string, interval string) bool
ActiveIn reports whether the customer had a usage event in the given bucket.
func (CustActivity) ActiveSince ¶
func (ca CustActivity) ActiveSince(cut time.Time) bool
ActiveSince reports whether the customer had a usage event at or after cut.
type GrantOut ¶
type GrantOut struct {
Status string `json:"status"`
Msg string `json:"msg"`
Data *GrantResult `json:"data"`
}
GrantOut is the envelope of every credit-grant op. There is one shape because there is ONE credit-write path.
func ApplyGrant ¶
func ApplyGrant(s *cloud.Service[State], c *zip.Ctx, org string, req CreditRequest) (*GrantOut, error)
ApplyGrant validates the amount + target org, deposits into the org's commerce ledger (trial vs prepaid by source), and records the tamper-evident audit row. One path, one way to grant.
Two refusals carry a NON-200 status, set on c before the envelope is returned: an unknown org is 404, and a deployment with no durable audit store is 503. Both keep the envelope body — the status is the addition, not a different contract.
type GrantResult ¶
type GrantResult struct {
// Org is the tenant whose ledger was credited.
Org string `json:"org"`
// Subject is the ACCOUNT the credit landed on inside that ledger: the org slug for
// a pooled org, "<org>/<name>" for a member of a per-member one. It is echoed
// because the operator does not choose it — account.Payer does — so naming a
// member of a pooled org credits the pool and the receipt has to say so.
Subject string `json:"subject"`
// GrantedCents is the amount actually credited.
GrantedCents int64 `json:"grantedCents"`
// Currency is the lower-cased ISO code the grant was denominated in.
Currency string `json:"currency"`
// Source is the money bucket: "trial" (non-cash comp) or "prepaid" (real money).
Source string `json:"source"`
// BalanceCents is the account balance AFTER the grant, in whole cents.
BalanceCents int64 `json:"balanceCents"`
// BalanceExact is that same balance at full 18-decimal precision, so a sub-cent
// debit is visible rather than rounded away.
BalanceExact string `json:"balanceExact"`
// TransactionID is the ledger entry id, for reconciliation against commerce.
TransactionID string `json:"transactionId"`
}
GrantResult is what a credit grant DID — the receipt both grant ops answer with.
type None ¶
type None struct{}
None is the input of an op that takes none: no body, no query, no path param. It is shared rather than redeclared per op because an empty struct carries no contract to document — the ops that DO take input each declare their own named In.
type SeriesPoint ¶
SeriesPoint is one bucketed point (count OR cents, per the series). T is the bucket key (RFC3339 date / "2006-01" month).
func SpendSeries ¶
func SpendSeries(acts []CustActivity, since, now time.Time, interval string) []SeriesPoint
SpendSeries buckets fleet usage cents into a continuous series over since..now. Shared by the analytics usage/revenue trend and the revenue board's spend trend (one implementation, DRY). A bucket with no usage is an honest 0, not a gap.
type SourceStatus ¶
type SourceStatus struct {
Name string `json:"name"`
OK bool `json:"ok"`
Rows int `json:"rows"`
Error string `json:"error"`
At string `json:"at"`
}
SourceStatus is the freshness of one upstream the aggregator pulls from (overview.sources[] / revenue.sources[] / finance.sources[] / analytics.sources[]).
type State ¶
type State struct {
IAM *iam.Client
Commerce *commerce.Client
Health *health.Client
DO *digitalocean.Client
AdminOrg string
AuditStore *audit.Recorder
// WLTenants is the fail-closed allowlist of enabled WHITE-LABEL TENANT orgs — the
// resellers/brands whose OWN-org admins may reach the org-scoped cockpit panels
// (GuardScoped) at admin.<brand>. It is the second admission tier next to the admin
// org: a SuperAdmin (owner == AdminOrg) is cross-tenant and never consults this set;
// EVERY other caller must be an admin of an org that is IN this set, and is then
// hard-scoped to that org's subtree. Seeded ONCE from ADMIN_WL_TENANT_ORGS at Mount
// and otherwise immutable, so the decision is a deliberate, git-/KMS-auditable
// onboarding — never self-service. EMPTY by default: with no entry, NO customer
// org-admin is admitted (only SuperAdmins reach the cockpit), so a mis-set/absent
// env fails CLOSED, never open.
WLTenants map[string]bool
}
State is admin's own data: the resolved upstream clients + the admin org for this deployment. admin holds NO Base shared deps (it fans out over HTTP replaying the caller's own creds); the embedded cloud.Base carries only the mount-time logger.
AuditStore is cloud's OWN tamper-evident audit store (nil when unconfigured, in which case /v1/admin/audit falls back to the IAM get-records proxy). Serve builds it and hands it over via deps.Audit.
func (State) IsWhiteLabelTenant ¶
IsWhiteLabelTenant reports whether org is an enabled white-label tenant — the ONE place the WL admission decision is read. Nil/empty set ⇒ always false (fail-closed): no org is a WL tenant until explicitly enabled. The org is matched VERBATIM (only trimmed), the same owner key principal.Org yields, so folding can never collapse a distinct owner into an enabled one.
type TenantScope ¶
TenantScope is a request's resolved visibility window. Super and Orgs are the two mutually exclusive views: a SuperAdmin sees all tenants (Orgs ignored); anyone else sees exactly Orgs (their own subtree).
func ResolveScope ¶
ResolveScope derives the request's tenant window from the SANITIZED identity only — never a client-forgeable field. A SuperAdmin (c.IsAdmin(), owner == admin org) is cross-tenant; any other caller is pinned to the subtree of their own (sanitized) org.
func (TenantScope) ScopedToOrg ¶
func (t TenantScope) ScopedToOrg(o string) bool
ScopedToOrg reports whether the scope admits reads for org o. Super admits every org; a scoped caller admits only orgs in their subtree. Used by panels that filter an upstream list (e.g. bases) rather than fanning out per-org.