entitlements

package
v1.799.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Mount

func Mount(app *zip.App, deps cloud.Deps) error

Mount registers the entitlements surface on app per HIP-0106.

func Shutdown

func Shutdown(_ context.Context) error

Shutdown releases the entitlements store. Idempotent.

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) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) Disable

func (s *Store) Disable(ctx context.Context, org, product string) error

Disable turns product off for org (idempotent — disabling an already-off product is a no-op, never an error).

func (*Store) Enable

func (s *Store) Enable(ctx context.Context, org, product, by string, atUnix int64) error

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).

func (*Store) List

func (s *Store) List(ctx context.Context, org string) ([]string, error)

List returns the enabled product ids for org, sorted (a stable contract the console can diff). An org that has enabled nothing returns an empty slice, not an error — "no products enabled yet" is a valid state, never a 404.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL