Documentation
¶
Overview ¶
Package entitlements is the per-org product-enablement plane for the unified Hanzo Cloud binary: the /v1/orgs/:org/entitlements surface the console's paid- product sidebar reads to decide which products to SHOW, and org owners / super admins write to TURN a product on or off.
Surface (all org-scoped; /v1 only):
GET /v1/orgs/:org/entitlements -> { "enabled": ["engine","chat",...] }
POST /v1/orgs/:org/entitlements { "add":[...], "remove":[...] } -> { "enabled":[...] }
TWO AUTHORITIES, NEVER BRAIDED.
- ENABLEMENT (this store): which products the org has toggled on. The org's intent. Durable per-org SQLite ({DataDir}/entitlements.db), (org,product) key.
- ENTITLEMENT (commerce): which products the org's plan/subscription grants. The billing truth. Read via deps.Commerce.CheckEntitlement at WRITE time.
A product may only be ENABLED if it is ENTITLED — so a non-super-admin can only switch on what the org already pays for; enabling never spends new money (a plan upgrade happens in commerce, not here). DISABLING is always allowed (turning a product off is never gated). A SUPER ADMIN (owner==AdminOrg) BYPASSES the commerce gate — the operator can comp/grant any product to any org — and may target ANY :org.
ORG SCOPING mirrors clients/kms (/v1/kms/orgs/:org): {:org} must equal the caller's VALIDATED org (c.Org()), unless the caller is a super admin (c.IsAdmin(), minted only for owner==AdminOrg by SanitizeIdentity — never client-forgeable), who may act on any org. A bearer-less forge (X-Org-Id restored, no X-User-Id) fails the principal.Validated gate → 403. There is no path a caller reads or writes another org's entitlements.
Index ¶
- func Mount(app *zip.App, deps cloud.Deps) error
- func Shutdown(_ context.Context) error
- type Store
- func (s *Store) Apply(ctx context.Context, org string, add, remove []string, by string, atUnix int64) ([]string, error)
- func (s *Store) Close() error
- func (s *Store) Disable(ctx context.Context, org, product string) error
- func (s *Store) Enable(ctx context.Context, org, product, by string, atUnix int64) error
- func (s *Store) List(ctx context.Context, org string) ([]string, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the entitlements metastore over one SQLite file ({DataDir}/entitlements.db). Org-scoping is the (org, product) key.
func (*Store) Apply ¶
func (s *Store) Apply(ctx context.Context, org string, add, remove []string, by string, atUnix int64) ([]string, error)
Apply enables `add` and disables `remove` for org in ONE transaction, so a batch mutation is all-or-nothing (the console never sees a half-applied set). It returns the resulting enabled set. add/remove are already product-shape validated and entitlement-gated by the caller; a product in BOTH lists is removed (remove wins — the explicit "off" is the safer resolution).
func (*Store) Disable ¶
Disable turns product off for org (idempotent — disabling an already-off product is a no-op, never an error).
func (*Store) Enable ¶
Enable turns product on for org (idempotent — re-enabling keeps the original enabled_at, refreshing only who last enabled it). `by` is the actor's user id, recorded for audit; empty is allowed (e.g. a super-admin machine grant).