allowance

package
v0.2.45 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: MPL-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package allowance is a tenant's own fuel budgets inside its tenant budget.

An allowance is a named budget a tenant defines for part of its work — one per customer, team, agent or feature, whatever the tenant's own unit is — as `{name, fuel, per}`: so much fuel per UTC hour, day or month. A request enters an allowance (`txco://allowance/enter`), and from then on its fuel is charged to that allowance as well as to the tenant. When the window's fuel is spent the chassis refuses the request through the shared admission marker (`429 allowance_exhausted`, Retry-After = the window's end), the same path a tenant rate limit takes, so every head renders it in its own protocol.

The tenant's own budget is untouched: allowance usage is still the tenant's usage, and whichever runs out first refuses.

Definitions and counters live in the tenant's KV under chassis-reserved namespaces, so the author-facing txco://kv/* ops can neither read nor reset them. Counters are charged from the usage path (Tee), so everything that bills a request — its own line, resumed continuation segments, attachment heartbeats — fills the allowance it ran under.

Index

Constants

View Source
const (
	NamespaceDefs = kv.ReservedNamespacePrefix + ".allowance"
	NamespaceUsed = kv.ReservedNamespacePrefix + ".allowance.used"
)

Chassis-reserved KV namespaces (kv.IsReservedNamespace), so the author- facing txco://kv/* ops refuse them: definitions keyed by name, counters keyed by "<name>@<window id>".

View Source
const DefaultPeriod = Day

DefaultPeriod windows the counters of an allowance that was entered but never defined (metered, not limited).

View Source
const FlushInterval = time.Second

FlushInterval is how often the Tee writes accumulated charges to the KV. It bounds how stale an allowance's counter is for the next request's check.

Variables

This section is empty.

Functions

func ValidName

func ValidName(s string) bool

ValidName: 1–64 of [a-z0-9._-], starting with a letter or digit. No '/', which a KV key segment cannot hold and which is kept for nesting later.

Types

type Def

type Def struct {
	Name string `json:"name"`
	Fuel int64  `json:"fuel"`
	Per  Period `json:"per"`
}

Def is one allowance: Fuel per Per. Fuel is the limit; it must be positive.

func (Def) Validate

func (d Def) Validate() error

Validate checks the name, the limit and the period.

type Period

type Period string

Period is an allowance's window: fixed UTC hours, days or months.

const (
	Hour  Period = "hour"
	Day   Period = "day"
	Month Period = "month"
)

func ParsePeriod

func ParsePeriod(s string) (Period, error)

ParsePeriod accepts "hour" | "day" | "month".

func (Period) Window

func (p Period) Window(t time.Time) (id string, start, end time.Time)

Window is the window containing t: an id that names it in a counter key, and its UTC bounds [Start, End).

type Status

type Status struct {
	Name     string
	Defined  bool
	Fuel     int64 // the limit; 0 when not defined
	Per      Period
	Used     int64
	ResetsAt time.Time
}

Status is one allowance's current window. Defined is false for a name that was never set: such an allowance is metered (Used counts) but not limited.

func (Status) Exhausted

func (s Status) Exhausted() bool

Exhausted reports a defined allowance with nothing left in its window.

func (Status) Remaining

func (s Status) Remaining() int64

Remaining is the fuel left in the window (never negative); 0 when not defined — check Defined first.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store reads and writes allowances in a tenant's KV. Safe for concurrent use.

func NewStore

func NewStore(k *kv.KV) *Store

NewStore returns a Store over k. Pass a kv.KV with no TTL ceiling: a month window's counter must outlive any operator cap on author TTLs.

func (*Store) Charge

func (s *Store) Charge(ctx context.Context, tenant, name string, fuel int64) error

Charge adds fuel to the allowance's current window. The window follows the definition's period (cached briefly); an undefined allowance is counted by DefaultPeriod.

func (*Store) Def

func (s *Store) Def(ctx context.Context, tenant, name string) (Def, bool, error)

Def reads one definition (fresh, not cached).

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, tenant, name string) error

Delete removes a definition. Its counters expire on their own.

func (*Store) List

func (s *Store) List(ctx context.Context, tenant, after string, limit int) (defs []Def, next string, err error)

List returns one page of definitions in name order after the cursor; next is "" when there are no more.

func (*Store) Set

func (s *Store) Set(ctx context.Context, tenant string, d Def) error

Set creates or replaces a definition. Its counters are kept: raising the limit mid-window frees what is left of the window at once.

func (*Store) Status

func (s *Store) Status(ctx context.Context, tenant, name string) (Status, error)

Status reads a definition and its current window's counter in one round trip. The counter key depends on the period, which depends on the definition, so all three windows' counters are read alongside it.

type Tee

type Tee struct {
	// contains filtered or unexported fields
}

Tee is a usage.Sink in front of the configured one. Every event goes on to the next sink unchanged; a billable event that ran under an allowance also adds its fuel to that allowance's counter. Charges are summed in memory and written once per FlushInterval per allowance, so a busy allowance costs one KV increment a second, not one per request.

func NewTee

func NewTee(next usage.Sink, store *Store, logger *zap.Logger) *Tee

NewTee wraps next (which may be nil) and starts the flush loop.

func (*Tee) Close

func (t *Tee) Close(ctx context.Context) error

Close stops the loop, writes what is pending within ctx, then closes the wrapped sink.

func (*Tee) Name

func (t *Tee) Name() string

Name reports the wrapped sink's name: the tee is not a backend of its own.

func (*Tee) WriteEvent

func (t *Tee) WriteEvent(ev usage.UsageEvent)

WriteEvent forwards ev and, when it is billable fuel spent under an allowance, adds it to the pending charges.

Jump to

Keyboard shortcuts

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