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 ApplyGrant(s *cloud.Service[State], c *zip.Ctx, org string, req CreditRequest) 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 CHFloat64(v any) float64
- 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 Fail(c *zip.Ctx, msg string) error
- func FindOrg(s *cloud.Service[State], ctx context.Context, cr iam.Creds, org string) (*iam.Org, error)
- func Guard(s *cloud.Service[State], h Handler) zip.Handler
- func GuardScoped(s *cloud.Service[State], h Handler) zip.Handler
- 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 OK(c *zip.Ctx, data any) error
- func OKList(c *zip.Ctx, rows any, total int) error
- func OKRaw(c *zip.Ctx, rows json.RawMessage, total int) error
- 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 WarehouseReady() bool
- func WarehouseSince(rangeLabel string) time.Time
- func WeekKey(t time.Time) string
- type CreditRequest
- type CustActivity
- type Handler
- type SeriesPoint
- type SourceStatus
- type State
- type TenantScope
- type TxnPoint
Constants ¶
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 ApplyGrant ¶
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.
func BillingEventsReady ¶ added in v1.801.115
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 ¶ added in v1.801.115
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 ¶ added in v1.801.115
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 CHTime ¶ added in v1.801.115
CHTime coerces a datastore DateTime (time.Time) to an RFC3339 UTC string.
func CHTimeLit ¶ added in v1.801.115
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 Fail ¶
Fail writes a { status:"error", msg } envelope. The operator's transport maps a non-ok envelope to a surfaced error (never a fabricated value).
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 Guard ¶
Guard wraps a handler with the SuperAdmin gate. Fail-closed: any request whose validated identity is not a SuperAdmin (X-User-IsAdmin != "true", which SanitizeIdentity sets only for owner == AdminOrg) is refused 403 before the handler — no upstream is touched, no data leaks.
func GuardScoped ¶
GuardScoped is the gate for the ORG-SCOPED panels (me/overview/orgs/users/usage/ analytics/bases + spend-caps). It admits a SuperAdmin (principal.IsSuperAdmin) OR an admin of an ENABLED WHITE-LABEL TENANT org (principal.IsOrgAdmin pinned to its validated org AND that org ∈ State.WLTenants), and the handler then scopes every read to ResolveScope(c) — so a non-super caller passes the gate but the DATA layer hard-limits them to their own org subtree. Cross-tenant reads are impossible for a non-super caller regardless of input.
THREE admission facts, ALL required for the non-super tier — the escalation line:
- X-User-IsOrgAdmin (principal.IsOrgAdmin — "admin of my own org", unforgeable because SanitizeIdentity strips it on ingress and re-mints 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 — the boundary PINS a non-super caller's X-Org-Id to their own owner, never client-chosen), AND
- that org is an ENABLED WL tenant (State.IsWhiteLabelTenant — the fail-closed allowlist). This is the tier the old gate was MISSING: it admitted ANY org-admin, so any customer's own-org admin could open the cockpit. Now an org-admin whose org is not an enabled WL tenant is REFUSED — the SAME 403 a non-admin member or a forged-X-Org-Id anonymous caller gets. A SuperAdmin never consults the allowlist.
Fail-closed everywhere: nil/empty WLTenants ⇒ ONLY SuperAdmins reach these panels.
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 OKRaw ¶
OKRaw writes a { status:"ok", data:<raw>, data2:total } envelope, forwarding an IAM payload verbatim so its exact wire shape (Role, Application, Record, User) reaches the operator field-for-field.
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 ¶ added in v1.801.115
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 WarehouseReady ¶ added in v1.801.115
func WarehouseReady() bool
WarehouseReady reports whether the shared datastore ledger is connected, the gate every fleet read checks first (honest-empty when false).
func WarehouseSince ¶ added in v1.801.115
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 Handler ¶
Handler is the admin handler shape: a free function over the shared kernel. Every domain handler is `func h(s *cloud.Service[core.State], c *zip.Ctx) error`.
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 ¶ added in v1.801.218
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.