tenant

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package tenant defines the multi-tenant management types and service.

Index

Constants

This section is empty.

Variables

View Source
var ErrDuplicate = errors.New("nexus: tenant already exists")

ErrDuplicate reports an existing tenant ID or slug.

View Source
var ErrInUse = errors.New("nexus: tenant is in use")

ErrInUse reports that a tenant cannot be deleted because it still has keys or usage history. Disable it instead.

View Source
var ErrInvalid = errors.New("nexus: invalid tenant input")

ErrInvalid wraps every input the tenant service refuses, such as a missing name or slug.

View Source
var ErrNotFound = errors.New("nexus: tenant not found")

ErrNotFound reports that no tenant matched. Stores return it from FindByID, FindBySlug and Update, never a nil tenant with a nil error.

Functions

This section is empty.

Types

type Config

type Config struct {
	// AllowedModels, when not empty, limits the tenant to these models. A
	// request is admitted when the model it asked for, or the model an
	// alias resolved it to, is listed. Names are case-sensitive and match
	// exactly; surrounding spaces are trimmed when the tenant is saved. An
	// alias with several targets is checked against the target picked for
	// that request, so list every target when it matters.
	AllowedModels []string `json:"allowed_models,omitempty"`
	// BlockedModels refuses a request when the model it asked for, or the
	// model an alias resolved it to, is listed. Blocks win over allows. An
	// alias with several targets is checked against the target picked for
	// that request, so list every target when it matters.
	BlockedModels []string `json:"blocked_models,omitempty"`
	// DefaultModel is the model a request gets when it names none. It is
	// filled before alias resolution, so it may be an alias.
	DefaultModel string `json:"default_model,omitempty"`
	// RoutingStrategy is stored for the dashboard; not enforced by the
	// gateway yet.
	RoutingStrategy string `json:"routing_strategy,omitempty"`
	// GuardrailPolicy is stored for the dashboard; not enforced by the
	// gateway yet.
	GuardrailPolicy string `json:"guardrail_policy,omitempty"`
	// CacheEnabled false turns the response cache off for this tenant, for
	// reading and for writing. Nil follows the gateway setting.
	CacheEnabled *bool             `json:"cache_enabled,omitempty"`
	Metadata     map[string]string `json:"metadata,omitempty"`
}

Config holds per-tenant overrides.

type CreateInput

type CreateInput struct {
	Name     string            `json:"name"`
	Slug     string            `json:"slug"`
	Quota    *Quota            `json:"quota,omitempty"`
	Config   *Config           `json:"config,omitempty"`
	Metadata map[string]string `json:"metadata,omitempty"`
}

CreateInput is the input for creating a tenant.

type Events

type Events interface {
	EmitTenantCreated(ctx context.Context, tenantID id.TenantID)
	EmitTenantDisabled(ctx context.Context, tenantID id.TenantID)
}

Events receives tenant lifecycle events. *plugin.Registry satisfies it.

type InUseFunc

type InUseFunc func(ctx context.Context, tenantID string) (bool, error)

InUseFunc reports whether a tenant still has keys that belong to it. The gateway builds it from the key store. It is a func, not the key store, because the key package imports this one.

type ListOptions

type ListOptions struct {
	Status string `json:"status,omitempty"`
	Search string `json:"search,omitempty"`
	Limit  int    `json:"limit,omitempty"`
	Cursor string `json:"cursor,omitempty"`
}

ListOptions configures tenant listing. Search matches name or slug, anywhere, ignoring case. Lists are newest first and cursor paged.

type ListResult

type ListResult struct {
	Items      []*Tenant `json:"items"`
	NextCursor string    `json:"next_cursor"`
}

ListResult is one page of tenants. NextCursor is "" on the last page.

type Option

type Option func(*service)

Option configures the tenant service.

func WithEvents

func WithEvents(e Events) Option

WithEvents reports created tenants, and tenants that leave active.

func WithInUse

func WithInUse(f InUseFunc) Option

WithInUse makes Delete refuse, with ErrInUse, a tenant the check reports as in use. A store may refuse too: Postgres rejects a delete that would orphan a key or a usage row, and says so with the same error. Without a check the service leaves keys to the store.

type Quota

type Quota struct {
	RPM              int       `json:"rpm"`                // requests per minute
	TPM              int       `json:"tpm"`                // tokens per minute
	DailyRequests    int       `json:"daily_requests"`     // max requests per day (0 = unlimited)
	MonthlyBudgetUSD money.USD `json:"monthly_budget_usd"` // max spend per month, exact; zero means no budget
	MaxTokensPerReq  int       `json:"max_tokens_per_req"` // max tokens per single request

	// Streaming-specific limits. The gateway enforces them by default,
	// through the stream lifecycle stage reading the tenant's quota.
	MaxStreamDuration time.Duration `json:"max_stream_duration,omitempty"` // wall-clock cap per stream (0 = unlimited)
	MaxStreamTokens   int           `json:"max_stream_tokens,omitempty"`   // output-token cap per stream (0 = unlimited)
}

Quota defines limits for a tenant.

type Service

type Service interface {
	Create(ctx context.Context, input *CreateInput) (*Tenant, error)
	Get(ctx context.Context, id string) (*Tenant, error)
	GetBySlug(ctx context.Context, slug string) (*Tenant, error)
	Update(ctx context.Context, id string, input *UpdateInput) (*Tenant, error)
	Delete(ctx context.Context, id string) error
	List(ctx context.Context, opts *ListOptions) (*ListResult, error)
	UpdateQuota(ctx context.Context, id string, quota *Quota) error
	SetStatus(ctx context.Context, id string, status Status) error
}

Service manages tenant lifecycle.

func NewService

func NewService(store Store, opts ...Option) Service

NewService creates a new tenant service.

type Status

type Status string

Status represents the tenant's current state.

const (
	StatusActive    Status = "active"
	StatusDisabled  Status = "disabled"
	StatusSuspended Status = "suspended"
)

type Store

type Store interface {
	Insert(ctx context.Context, t *Tenant) error
	FindByID(ctx context.Context, id string) (*Tenant, error)
	FindBySlug(ctx context.Context, slug string) (*Tenant, error)
	Update(ctx context.Context, t *Tenant) error
	Delete(ctx context.Context, id string) error
	List(ctx context.Context, opts *ListOptions) (*ListResult, error)
}

Store is the persistence interface for tenants. Finders and Update return ErrNotFound when no tenant matches; Delete of an unknown id is a no-op.

type Tenant

type Tenant struct {
	ID        id.TenantID       `json:"id"`
	Name      string            `json:"name"`
	Slug      string            `json:"slug"` // URL-safe identifier
	Status    Status            `json:"status"`
	Quota     Quota             `json:"quota"`
	Config    Config            `json:"config"`
	Metadata  map[string]string `json:"metadata,omitempty"`
	CreatedAt time.Time         `json:"created_at"`
	UpdatedAt time.Time         `json:"updated_at"`
}

Tenant represents an isolated customer / team / project.

type UpdateInput

type UpdateInput struct {
	Name     *string           `json:"name,omitempty"`
	Quota    *Quota            `json:"quota,omitempty"`
	Config   *Config           `json:"config,omitempty"`
	Metadata map[string]string `json:"metadata,omitempty"`
}

UpdateInput is the input for updating a tenant.

Jump to

Keyboard shortcuts

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