catalog

package
v0.20.10 Latest Latest
Warning

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

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

README

Catalog

The catalog package defines the list of modules and add‑ons that can be purchased in Openlane. These modules are created and added to Stripe subscriptions if the feature is enabled. Each feature describes its billing information and any usage limits that are granted when enabled. Catalog data is stored in catalog.yaml and loaded at runtime to reconcile Stripe products and expose the available modules through the API.

Overview

A catalog entry contains the following pieces:

  • Modules – first‑class portions of the platform that customers subscribe to. At least one module is usually required. See the modules vs. addons section for explanation for a comparison with add‑ons.
  • Add‑ons – optional features that enhance a module. They often provide additional usage or automation in small increments.
  • Billing – one or more prices for a feature, defined by interval and amount. Prices are matched in Stripe by lookup key and metadata rather than hard coded IDs.
  • Usage – optional limits (e.g. evidence_storage_gb) granted when the feature is enabled.

The package exposes helper functions to load and save catalogs, validate prices against Stripe and ensure any missing products or prices are created. These packages are intended to be used for the Openlane Saas Product, so you would not enable this functionality running it as an open source project. This means that when you run the server WITHOUT the entitlements option enabled, there will be no checks around features, so access to any feature of the platform is not gated separately.

Flow
graph TD
    A[Catalog structs in catalog.go] --> B[generate catalog.schema.json]
    B --> C[catalog.yaml]
    C --> D[cmd/catalog syncs with Stripe]
    D --> E[Stripe products and prices]
    D --> C
    E --> F[Stripe webhook]
    F --> G[OpenFGA tuples]
    G --> H[Ent hooks & policies]

Modules vs. Add‑ons

Kind What it is Typical price Core to the product? UI placement Examples
module A first‑class, standalone slice of the platform. Customers normally subscribe to at least one module to get value. $20–$100 / mo Yes – at least one required Primary cards in signup & pricing page compliance, trust_center
addon An optional enhancement that augments a module. Often usage‑based or small flat fee. $1–$10 / mo No – opt‑in “Extras / Marketplace” or Billing settings vanity_domain, extra seats
Why we keep the lists separate
  • Positioning: Modules appear in marketing copy as base offerings; add‑ons are upsells.
  • Off‑boarding: Cancelling the last module should close the subscription; removing an add‑on should not.
  • Visibility controls: Add‑ons are frequently beta or private audience.
  • Pricing UI: Front‑end renders modules and add‑ons in distinct sections for clarity.

Implementation‑wise, the two kinds are identical Go structs; the separation only affects UX.

JSON Schema and Code Generation

Two small utilities live in the genjsonschema and genyaml directories.

go run genjsonschema/catalog_schema.go   # generates genjsonschema/catalog.schema.json
go run genyaml/yamlgen.go                # converts catalog.yaml into Go code

The generated JSON schema is used by LoadCatalog to validate the YAML format. Running go generate ./pkg/catalog (or task catalog:genjsonschema and task catalog:genyaml) will update both artifacts.

CLI Utilities

Under cmd/catalog are helper commands for working with the catalog and Stripe:

  • catalog – compares catalog.yaml with your Stripe account and optionally creates missing products or prices. Use --stripe-key to supply the API key.
  • pricemigrate – tags a replacement price and can migrate subscriptions from one price ID to another.

These tools are meant for internal maintenance but are useful when seeding a new Stripe environment or validating changes.

Catalog Versioning

SaveCatalog writes the catalog back to disk and manages version bumps. If the contents change, the patch version is incremented and a SHA256 of the version string is stored. This hash is checked by IsCurrent() to verify the catalog on disk matches its declared version.

Example

c, err := catalog.LoadCatalog("./pkg/catalog/catalog.yaml")
if err != nil {
    log.Fatal(err)
}

// Ensure products and prices exist in Stripe and update PriceID fields
if err := c.EnsurePrices(ctx, stripeClient, "usd"); err != nil {
    log.Fatal(err)
}

diff, err := c.SaveCatalog("./pkg/catalog/catalog.yaml")
if err != nil {
    log.Fatal(err)
}
fmt.Println(diff)

Features Subpackage

The features directory contains helpers for caching enabled features for an organization. Cache is a small wrapper around Redis that stores a set of feature names keyed by organization ID. Entries expire independently from session data and can be fetched with Get or written with Set.

c := features.NewCache(redisClient, time.Minute)
_ = c.Set(ctx, "org1", []string{"evidence", "search"})
feats, _ := c.Get(ctx, "org1")

Use features.WithCache and features.CacheFromContext to make the cache available throughout a request lifecycle.

Documentation

Overview

Package catalog provides the core functionality for managing and validating the catalog of products, features, and prices in the Openlane project

Index

Constants

View Source
const ManagedByKey = "managed_by"

ManagedByKey is the metadata key applied to Stripe resources created via the catalog

View Source
const ManagedByValue = "module-manager"

ManagedByValue identifies objects managed by the catalog automation

Variables

View Source
var (
	// ErrCatalogValidationFailed is returned when the catalog fails validation
	ErrCatalogValidationFailed = errors.New("catalog validation failed")
	// ErrProductMissingFeature is returned when a product is missing a required feature
	ErrProductMissingFeature = errors.New("product missing required feature")
	// ErrYamlToJSONConversion = errors.New("failed to convert YAML to JSON for catalog validation")
	ErrYamlToJSONConversion = errors.New("failed to convert YAML to JSON for catalog validation")
	// ErrMatchingPriceNotFound = errors.New("matching price not found for feature")
	ErrMatchingPriceNotFound = errors.New("matching price not found for feature")
	// ErrFailedToCreateProduct = errors.New("failed to create product in Stripe")
	ErrFailedToCreateProduct = errors.New("failed to create product in Stripe")
	// ErrFailedToCreatePrice = errors.New("failed to create price in Stripe")
	ErrFailedToCreatePrice = errors.New("failed to create price in Stripe")
	// ErrContextandClientRequired = errors.New("context and client are required for catalog operations"
	ErrContextandClientRequired = errors.New("context and client are required for catalog operations")
)

Functions

This section is empty.

Types

type Billing

type Billing struct {
	Prices []Price `json:"prices" yaml:"prices" jsonschema:"description=List of price options for this feature"`
}

Billing contains one or more price options for a module or addon

type Catalog

type Catalog struct {
	Version string     `json:"version" yaml:"version" jsonschema:"description=Catalog version,example=1.0.0"`
	SHA     string     `json:"sha" yaml:"sha" jsonschema:"description=SHA of the catalog version"`
	Modules FeatureSet `json:"modules" yaml:"modules" jsonschema:"description=Set of modules available in the catalog"`
	Addons  FeatureSet `json:"addons" yaml:"addons" jsonschema:"description=Set of addons available in the catalog"`
}

Catalog contains all modules and addons offered by Openlane

func LoadCatalog

func LoadCatalog(path string) (*Catalog, error)

LoadCatalog reads and parses a Catalog definition from disk.

func (*Catalog) EnsurePrices

func (c *Catalog) EnsurePrices(ctx context.Context, sc *entitlements.StripeClient, currency string) error

EnsurePrices verifies prices exist in Stripe and creates them when missing. New products are created using the feature display name and description. Matching is performed by unit amount, interval, nickname, lookup key and metadata instead of a fixed price ID. The discovered Stripe price ID is stored back in the catalog struct but not persisted to disk.

func (*Catalog) IsCurrent

func (c *Catalog) IsCurrent() bool

IsCurrent reports whether the catalog SHA matches its version.

func (*Catalog) SaveCatalog

func (c *Catalog) SaveCatalog(path string) (string, error)

SaveCatalog writes the catalog to disk in YAML format, as well as computing and updating the SHA

func (*Catalog) ValidatePrices

func (c *Catalog) ValidatePrices(ctx context.Context, sc *entitlements.StripeClient) error

ValidatePrices ensures every feature's price attributes match a Stripe price. Matching considers unit amount, interval, nickname, lookup key and metadata.

func (*Catalog) Visible

func (c *Catalog) Visible(audience string) *Catalog

Visible returns modules and addons filtered by audience

type Feature

type Feature struct {
	DisplayName string  `` /* 127-byte string literal not displayed */
	Description string  `` /* 171-byte string literal not displayed */
	Billing     Billing `json:"billing" yaml:"billing" jsonschema:"description=Billing information for the feature"`
	Audience    string  `` /* 140-byte string literal not displayed */
	Usage       *Usage  `json:"usage,omitempty" yaml:"usage,omitempty" jsonschema:"description=Usage limits granted by the feature"`
}

Feature defines a purchasable module or addon feature

type FeatureSet

type FeatureSet map[string]Feature

FeatureSet is a mapping of feature identifiers to metadata

type Price

type Price struct {
	Interval   string            `json:"interval" yaml:"interval" jsonschema:"enum=year,enum=month,description=Billing interval for the price,example=month"`
	UnitAmount int64             `json:"unit_amount" yaml:"unit_amount" jsonschema:"description=Amount to be charged per interval,example=1000"`
	Nickname   string            `` /* 141-byte string literal not displayed */
	LookupKey  string            `` /* 180-byte string literal not displayed */
	Metadata   map[string]string `` /* 141-byte string literal not displayed */
	PriceID    string            `json:"price_id,omitempty" yaml:"price_id,omitempty" jsonschema:"description=Stripe price ID,example=price_1N2Yw2A1b2c3d4e5"`
}

Price describes a single price option for a module or addon

type Usage

type Usage struct {
	EvidenceStorageGB int64 `` /* 142-byte string literal not displayed */
	RecordCount       int64 `` /* 131-byte string literal not displayed */
}

Usage defines usage limits granted by a feature.

Directories

Path Synopsis
Package features handles feature entitlements
Package features handles feature entitlements
Package gencatalog is the output of parsing the catalog file
Package gencatalog is the output of parsing the catalog file
Package genjsonschema generates JSON schema files from Go structs for use in validating catalogs
Package genjsonschema generates JSON schema files from Go structs for use in validating catalogs
Package main inside of genyaml generates Go source files from a catalog YAML file
Package main inside of genyaml generates Go source files from a catalog YAML file

Jump to

Keyboard shortcuts

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