Documentation
¶
Overview ¶
Package store holds the gateway's persistent state: the tenant hierarchy (organisation → team → project), virtual keys (hashed), budgets and per-request usage records. Two implementations share one contract test: Memory (tests, trials; state is lost on restart) and Postgres (pgx).
The hierarchy mirrors the Slurm account tree (sacctmgr: accounts nested under a parent account, users associated with accounts): an organisation is an institution, a team a lab, a project a piece of work inside a team. Keys belong to a team and optionally to one of its projects.
Index ¶
- Constants
- Variables
- func GenerateAdminToken() (secret, id string, hash [32]byte)
- func GenerateKey() (secret, id string, hash [32]byte)
- func HashEqual(a, b [32]byte) bool
- func HashSecret(secret string) [32]byte
- func NewKeySecret() (secret string, hash [32]byte)
- func NormalizeUnit(u string) (string, error)
- func UnitName(u string) string
- func ValidateBudget(b Budget) error
- func ValidateDims(dims []string) ([]string, error)
- type AllowKey
- type Allowlist
- type AuditCheckpoint
- type AuditEntry
- type AuditHasher
- type Binding
- type Budget
- type BudgetKey
- type Claim
- type Duration
- type HAStateStore
- type HAStore
- type Key
- type Lease
- type Memory
- func (m *Memory) Allowlists(context.Context) ([]Allowlist, error)
- func (m *Memory) AuditAddCheckpoint(_ context.Context, c AuditCheckpoint) error
- func (m *Memory) AuditAppend(_ context.Context, e AuditEntry, hash AuditHasher) (AuditEntry, error)
- func (m *Memory) AuditCheckpoints(context.Context) ([]AuditCheckpoint, error)
- func (m *Memory) AuditPrune(_ context.Context, through int64) (int64, error)
- func (m *Memory) AuditRange(_ context.Context, from int64, limit int) ([]AuditEntry, error)
- func (m *Memory) Bindings(_ context.Context, user string) ([]Binding, error)
- func (m *Memory) Budgets(context.Context) ([]Budget, error)
- func (m *Memory) Close()
- func (m *Memory) CreateKey(_ context.Context, k Key) error
- func (m *Memory) CreateOrg(_ context.Context, name string) error
- func (m *Memory) CreateProject(_ context.Context, team, name string) error
- func (m *Memory) CreateTeam(_ context.Context, name, org string) error
- func (m *Memory) CreateUser(_ context.Context, name string) error
- func (m *Memory) CreateUserToken(_ context.Context, t UserToken) error
- func (m *Memory) DeleteAudit(seq int64)
- func (m *Memory) DeleteBudget(_ context.Context, k BudgetKey) error
- func (m *Memory) DisableOrg(_ context.Context, name string) error
- func (m *Memory) DisableProject(_ context.Context, team, name string) error
- func (m *Memory) DisableTeam(_ context.Context, name string) error
- func (m *Memory) DisableUser(_ context.Context, name string) error
- func (m *Memory) DueKeys(_ context.Context, now time.Time) ([]Key, error)
- func (m *Memory) GetKey(_ context.Context, id string) (Key, error)
- func (m *Memory) GetTeam(_ context.Context, name string) (Team, error)
- func (m *Memory) GetUser(_ context.Context, name string) (User, error)
- func (m *Memory) Grant(_ context.Context, b Binding) error
- func (m *Memory) IncreaseBudget(_ context.Context, k BudgetKey, amount float64, expiry time.Time) error
- func (m *Memory) InsertRecords(_ context.Context, recs []Record) error
- func (m *Memory) KeyByHash(_ context.Context, h [32]byte) (Key, error)
- func (m *Memory) ListKeys(_ context.Context, team string) ([]Key, error)
- func (m *Memory) ListOrgs(context.Context) ([]Org, error)
- func (m *Memory) ListProjects(_ context.Context, team string) ([]Project, error)
- func (m *Memory) ListTeams(_ context.Context, org string) ([]Team, error)
- func (m *Memory) ListUserTokens(_ context.Context, user string) ([]UserToken, error)
- func (m *Memory) ListUsers(context.Context) ([]User, error)
- func (m *Memory) MarkSoftAlert(_ context.Context, k BudgetKey, period time.Time) (bool, error)
- func (m *Memory) Ping(context.Context) error
- func (m *Memory) Records() []Record
- func (m *Memory) Report(ctx context.Context, from, to time.Time, dims []string) ([]ReportRow, error)
- func (m *Memory) RevokeKey(_ context.Context, id string) error
- func (m *Memory) RevokeUserToken(_ context.Context, user, id string) error
- func (m *Memory) RotateKey(_ context.Context, id string, newHash, expect [32]byte, ...) error
- func (m *Memory) SCIMCreate(_ context.Context, d SCIMDoc) error
- func (m *Memory) SCIMDelete(_ context.Context, kind, id string) error
- func (m *Memory) SCIMGet(_ context.Context, kind, id string) (SCIMDoc, error)
- func (m *Memory) SCIMList(_ context.Context, kind, name string) ([]SCIMDoc, error)
- func (m *Memory) SCIMReplace(_ context.Context, d SCIMDoc, ifVersion string) error
- func (m *Memory) SetAllowlist(_ context.Context, a Allowlist) error
- func (m *Memory) SetBudget(_ context.Context, b Budget) error
- func (m *Memory) SetFail(err error)
- func (m *Memory) SetKeyRotation(_ context.Context, id string, r Rotation) error
- func (m *Memory) SetTeamModels(_ context.Context, team string, models []string) error
- func (m *Memory) SetTeamTools(_ context.Context, team string, tools []string) error
- func (m *Memory) SpendRows(_ context.Context, since time.Time) ([]SpendRow, error)
- func (m *Memory) TamperAudit(seq int64, f func(*AuditEntry))
- func (m *Memory) TeamModels(context.Context) (map[string][]string, error)
- func (m *Memory) TeamTools(context.Context) (map[string][]string, error)
- func (m *Memory) Ungrant(_ context.Context, b Binding) error
- func (m *Memory) UserByTokenHash(_ context.Context, h [32]byte) (User, UserToken, error)
- type Org
- type OutboxAlert
- type Period
- type PoolUsage
- type Postgres
- func (p *Postgres) Allowlists(ctx context.Context) ([]Allowlist, error)
- func (p *Postgres) AuditAddCheckpoint(ctx context.Context, c AuditCheckpoint) error
- func (p *Postgres) AuditAppend(ctx context.Context, e AuditEntry, hash AuditHasher) (AuditEntry, error)
- func (p *Postgres) AuditCheckpoints(ctx context.Context) ([]AuditCheckpoint, error)
- func (p *Postgres) AuditPrune(ctx context.Context, through int64) (int64, error)
- func (p *Postgres) AuditRange(ctx context.Context, from int64, limit int) ([]AuditEntry, error)
- func (p *Postgres) Bindings(ctx context.Context, user string) ([]Binding, error)
- func (p *Postgres) Budgets(ctx context.Context) ([]Budget, error)
- func (p *Postgres) ClaimAlerts(ctx context.Context, n int, lease time.Duration) ([]OutboxAlert, error)
- func (p *Postgres) ClaimLease(ctx context.Context, c Claim) (Lease, error)
- func (p *Postgres) Close()
- func (p *Postgres) CreateKey(ctx context.Context, k Key) error
- func (p *Postgres) CreateOrg(ctx context.Context, name string) error
- func (p *Postgres) CreateProject(ctx context.Context, team, name string) error
- func (p *Postgres) CreateTeam(ctx context.Context, name, org string) error
- func (p *Postgres) CreateUser(ctx context.Context, name string) error
- func (p *Postgres) CreateUserToken(ctx context.Context, t UserToken) error
- func (p *Postgres) DeleteBudget(ctx context.Context, k BudgetKey) error
- func (p *Postgres) DisableOrg(ctx context.Context, name string) error
- func (p *Postgres) DisableProject(ctx context.Context, team, name string) error
- func (p *Postgres) DisableTeam(ctx context.Context, name string) error
- func (p *Postgres) DisableUser(ctx context.Context, name string) error
- func (p *Postgres) DropSealed(ctx context.Context, kind string, id [32]byte) error
- func (p *Postgres) DueKeys(ctx context.Context, now time.Time) ([]Key, error)
- func (p *Postgres) EnqueueAlert(ctx context.Context, key string, channels []string, payload []byte) error
- func (p *Postgres) FinishAlert(ctx context.Context, key, channel, state string, retryIn time.Duration, ...) error
- func (p *Postgres) GetKey(ctx context.Context, id string) (Key, error)
- func (p *Postgres) GetSealed(ctx context.Context, kind string, id [32]byte) ([]byte, bool, error)
- func (p *Postgres) GetTeam(ctx context.Context, name string) (Team, error)
- func (p *Postgres) GetUser(ctx context.Context, name string) (User, error)
- func (p *Postgres) Grant(ctx context.Context, b Binding) error
- func (p *Postgres) IncreaseBudget(ctx context.Context, k BudgetKey, amount float64, expiry time.Time) error
- func (p *Postgres) InsertRecords(ctx context.Context, recs []Record) error
- func (p *Postgres) KeyByHash(ctx context.Context, h [32]byte) (Key, error)
- func (p *Postgres) ListKeys(ctx context.Context, team string) ([]Key, error)
- func (p *Postgres) ListOrgs(ctx context.Context) ([]Org, error)
- func (p *Postgres) ListProjects(ctx context.Context, team string) ([]Project, error)
- func (p *Postgres) ListTeams(ctx context.Context, org string) ([]Team, error)
- func (p *Postgres) ListUserTokens(ctx context.Context, user string) ([]UserToken, error)
- func (p *Postgres) ListUsers(ctx context.Context) ([]User, error)
- func (p *Postgres) ListenKeys(ctx context.Context, connected func(), on func(payload string))
- func (p *Postgres) LoadUsage(ctx context.Context, pool string) ([]PoolUsage, error)
- func (p *Postgres) MarkSoftAlert(ctx context.Context, k BudgetKey, period time.Time) (bool, error)
- func (p *Postgres) NotifyKeys(ctx context.Context, payload string) error
- func (p *Postgres) Ping(ctx context.Context) error
- func (p *Postgres) PurgeSealed(ctx context.Context) error
- func (p *Postgres) PutSealed(ctx context.Context, kind string, id [32]byte, net string, sealed []byte, ...) error
- func (p *Postgres) ReleaseLease(ctx context.Context, pool, owner string, epoch int64) error
- func (p *Postgres) Report(ctx context.Context, from, to time.Time, dims []string) ([]ReportRow, error)
- func (p *Postgres) RevokeKey(ctx context.Context, id string) error
- func (p *Postgres) RevokeUserToken(ctx context.Context, user, id string) error
- func (p *Postgres) RotateKey(ctx context.Context, id string, newHash, expect [32]byte, ...) error
- func (p *Postgres) SCIMCreate(ctx context.Context, d SCIMDoc) error
- func (p *Postgres) SCIMDelete(ctx context.Context, kind, id string) error
- func (p *Postgres) SCIMGet(ctx context.Context, kind, id string) (SCIMDoc, error)
- func (p *Postgres) SCIMList(ctx context.Context, kind, name string) ([]SCIMDoc, error)
- func (p *Postgres) SCIMReplace(ctx context.Context, d SCIMDoc, ifVersion string) error
- func (p *Postgres) SaveUsage(ctx context.Context, pool, owner string, epoch int64, rows []PoolUsage) error
- func (p *Postgres) SetAllowlist(ctx context.Context, a Allowlist) error
- func (p *Postgres) SetBudget(ctx context.Context, b Budget) error
- func (p *Postgres) SetKeyRotation(ctx context.Context, id string, r Rotation) error
- func (p *Postgres) SetTeamModels(ctx context.Context, team string, models []string) error
- func (p *Postgres) SetTeamTools(ctx context.Context, team string, tools []string) error
- func (p *Postgres) SpendRows(ctx context.Context, since time.Time) ([]SpendRow, error)
- func (p *Postgres) TakeSealed(ctx context.Context, kind string, id [32]byte) ([]byte, bool, error)
- func (p *Postgres) TeamModels(ctx context.Context) (map[string][]string, error)
- func (p *Postgres) TeamTools(ctx context.Context) (map[string][]string, error)
- func (p *Postgres) Ungrant(ctx context.Context, b Binding) error
- func (p *Postgres) UsageRecordIDs(ctx context.Context) ([]string, error)
- func (p *Postgres) UserByTokenHash(ctx context.Context, h [32]byte) (User, UserToken, error)
- type Project
- type Record
- type ReportRow
- type Role
- type Rotation
- type SCIMDoc
- type ScopeKind
- type SealedLimits
- type SpendRow
- type Store
- type Team
- type User
- type UserToken
Constants ¶
const AdminTokenPrefix = "llma-"
AdminTokenPrefix starts every user admin token.
const GPUHours = "gpu_hours"
GPUHours is the unit of the costs of vLLM models, as the API and the reports spell it; it is stored as "".
const KeyPrefix = "llmf-"
KeyPrefix starts every virtual key, so a leaked key is recognisable by secret scanners.
const MaxReportSpan = 366 * 24 * time.Hour
MaxReportSpan bounds the period of one report: a report reads every record of its period.
Variables ¶
var ( ErrNotFound = errors.New("not found") ErrExists = errors.New("already exists") // ErrConflict reports an existing object that differs from the // request (e.g. a team that already belongs to another organisation). ErrConflict = errors.New("conflicts with an existing object") )
Errors.
var ErrAuditUpdate = errors.New("the audit log is append-only")
ErrAuditUpdate is what the PostgreSQL trigger raises on an UPDATE.
var ErrFenced = errors.New("store: fenced: the lease is held by a newer owner or has expired")
ErrFenced: the write came from an owner whose lease is no longer the current one (expired, or taken over with a higher epoch).
var ErrFull = errors.New("store: too many entries")
ErrFull: a bound on stored logins or sessions was reached.
var ReportDims = []string{"org", "lab", "team", "project", "user", "key", "model", "pool", "tag", "day"}
ReportDims lists the accepted dimensions, in canonical order: the hierarchy org → lab → team → project, then user, key, model, pool, tag, and day.
Functions ¶
func GenerateAdminToken ¶ added in v0.2.0
GenerateAdminToken returns a new admin token secret (256 random bits), its ID and its SHA-256.
func GenerateKey ¶
GenerateKey returns a new secret (256 random bits), its public ID and its SHA-256. The secret is shown once to the caller and never stored. An unsalted hash is enough for a uniformly random 256-bit secret: there is no dictionary to precompute.
func NewKeySecret ¶ added in v0.4.0
NewKeySecret makes a new secret for an existing key (rotation).
func NormalizeUnit ¶ added in v0.12.0
NormalizeUnit maps the spelled-out GPU-hours unit to "" and checks a currency unit: 1 to 16 letters, digits or underscores (USD, EUR, usd_cents…). Units are compared exactly.
func ValidateBudget ¶
ValidateBudget checks a budget before it is stored.
func ValidateDims ¶ added in v0.2.0
ValidateDims checks and orders report dimensions.
Types ¶
type AllowKey ¶ added in v0.4.0
type AllowKey struct {
Kind ScopeKind `json:"kind"` // ScopeOrg, ScopeTeam or ScopeKey
Name string `json:"name"`
}
AllowKey names what an IP allowlist applies to: an organisation, a team or a key (by ID).
type Allowlist ¶ added in v0.4.0
Allowlist is the set of client networks allowed for one scope.
func ValidateAllowlist ¶ added in v0.4.0
ValidateAllowlist checks the kind and canonicalises the prefixes.
type AuditCheckpoint ¶ added in v0.5.0
AuditCheckpoint signs the chain up to Seq, whose entry hash is Hash.
type AuditEntry ¶ added in v0.5.0
type AuditEntry struct {
// V is the canonical form the hash was computed with (package audit).
V int
Seq int64
Time time.Time // microsecond precision
Kind string // admin, auth, scim, system, guardrail, tool
Actor string
Roles string // JSON list of the actor's role bindings
Action string
Target string
Before string
After string
Status int
SourceIP string
RequestID string
PrevHash []byte
Hash []byte
}
AuditEntry is one recorded event. Before and After are JSON, kept as the exact bytes that were hashed.
type AuditHasher ¶ added in v0.5.0
type AuditHasher func(e *AuditEntry) []byte
AuditHasher computes an entry's hash once its Seq and PrevHash are known.
type Binding ¶ added in v0.2.0
type Binding struct {
User string `json:"user"`
Role Role `json:"role"`
Org string `json:"org,omitempty"`
Team string `json:"team,omitempty"`
}
Binding grants a role on a scope. Exactly the fields the role needs are set: none for proxy roles, Org for org_admin (and viewer of an org), Team for team_admin, member (and viewer of a team).
type Budget ¶
type Budget struct {
BudgetKey
Period Period `json:"period"`
MaxCost float64 `json:"max_cost"`
SoftLimit float64 `json:"soft_limit,omitempty"`
TempIncrease float64 `json:"temp_increase,omitempty"`
TempExpiry *time.Time `json:"temp_expiry,omitempty"`
}
Budget caps the cost of a scope per period, in its unit. A temporary increase adds to MaxCost until it expires; SoftLimit (0 = none) triggers one alert per period when the spend reaches it.
type BudgetKey ¶ added in v0.2.0
type BudgetKey struct {
Kind ScopeKind `json:"kind"`
Name string `json:"name"`
Model string `json:"model,omitempty"`
Unit string `json:"unit,omitempty"`
}
BudgetKey identifies a budget: a scope, optionally restricted to one model group (Model "" covers every model), in one cost unit (Unit "" is GPU-hours; "USD" or another currency code caps what an external provider charges). A budget counts only the costs of its own unit: GPU-hours and dollars are never added together.
type Claim ¶ added in v0.15.2
type Claim struct {
Pool, Owner, Addr string
// Held is the epoch the claimant owns (0: none).
Held int64
TTL time.Duration
// MayTake: the claimant may take an expired lease (it knows the
// pool's membership); MayTakeYielded: it may take a live lease whose
// owner yields (its own view of the membership is fresh).
MayTake, MayTakeYielded bool
// Yield, Capacity and Reserved are written with the claimant's lease.
Yield bool
Capacity int
Reserved string
}
Claim is one claim of a pool's lease.
type Duration ¶ added in v0.4.0
Duration is a time.Duration written as a Go duration string in JSON.
func (Duration) MarshalJSON ¶ added in v0.4.0
MarshalJSON writes "720h0m0s".
func (*Duration) UnmarshalJSON ¶ added in v0.4.0
UnmarshalJSON reads a Go duration string.
type HAStateStore ¶ added in v0.13.1
type HAStateStore interface {
// PutSealed stores sealed bytes under (kind, id) for ttl (by the
// store's clock), unless a limit is reached (ErrFull).
PutSealed(ctx context.Context, kind string, id [32]byte, net string, sealed []byte, ttl time.Duration, lim SealedLimits) error
// GetSealed reads a live entry.
GetSealed(ctx context.Context, kind string, id [32]byte) ([]byte, bool, error)
// TakeSealed reads and deletes a live entry (one use).
TakeSealed(ctx context.Context, kind string, id [32]byte) ([]byte, bool, error)
DropSealed(ctx context.Context, kind string, id [32]byte) error
// PurgeSealed deletes expired entries and old finished alerts.
PurgeSealed(ctx context.Context) error
// EnqueueAlert adds one alert per channel, once per key: a second
// enqueue of the same (key, channel) changes nothing.
EnqueueAlert(ctx context.Context, key string, channels []string, payload []byte) error
// ClaimAlerts takes up to n due alerts and hides them for lease (a
// replica that dies while sending leaves them to the next one).
ClaimAlerts(ctx context.Context, n int, lease time.Duration) ([]OutboxAlert, error)
// FinishAlert records a delivery attempt: sent, given up (failed), or
// retried after the given delay.
FinishAlert(ctx context.Context, key, channel, state string, retryIn time.Duration, lastErr string) error
// NotifyKeys broadcasts a revocation to every replica.
NotifyKeys(ctx context.Context, payload string) error
// ListenKeys calls on for every revocation until ctx ends, and
// connected each time it (re)connects (notifications sent while it was
// not connected are lost, PostgreSQL does not keep them).
ListenKeys(ctx context.Context, connected func(), on func(payload string))
}
HAStateStore is the shared state besides pool ownership.
type HAStore ¶ added in v0.13.1
type HAStore interface {
// ClaimLease renews the lease when the claimant holds it at epoch Held
// and it has not expired; takes it (epoch + 1) when it has expired (or
// does not exist) and MayTake, or when it is live but its owner yields
// and MayTakeYielded (Quiet); otherwise changes nothing. It returns the
// lease as it stands afterwards.
ClaimLease(ctx context.Context, c Claim) (Lease, error)
// ReleaseLease ends owner's lease at epoch now (graceful shutdown).
ReleaseLease(ctx context.Context, pool, owner string, epoch int64) error
// SaveUsage writes the owner's state, fenced: ErrFenced unless owner
// holds the live lease at epoch, checked under a row lock that a
// takeover must wait for.
SaveUsage(ctx context.Context, pool, owner string, epoch int64, rows []PoolUsage) error
// LoadUsage reads what the last owner persisted.
LoadUsage(ctx context.Context, pool string) ([]PoolUsage, error)
}
HAStore is what package ha needs from the store. Only PostgreSQL has it.
type Key ¶
type Key struct {
ID string `json:"id"`
Hash [32]byte `json:"-"`
// Org is resolved from the team when the key is read.
Org string `json:"org"`
Team string `json:"team"`
Project string `json:"project,omitempty"`
// Owner is the user who created the key as a team member; empty for
// team keys created by an administrator.
Owner string `json:"owner,omitempty"`
// Tags label every request made with the key (budgets and reports).
Tags []string `json:"tags,omitempty"`
Name string `json:"name"`
RPM int `json:"rpm"`
TPM int `json:"tpm"`
MaxInFlight int `json:"max_inflight"`
CreatedAt time.Time `json:"created_at"`
Revoked bool `json:"revoked"`
// PrevHash is the secret replaced by the last rotation, valid until
// PrevExpires (zero: no previous secret).
PrevHash [32]byte `json:"-"`
PrevExpires time.Time `json:"prev_expires_at,omitzero"`
RotatedAt time.Time `json:"rotated_at,omitzero"`
// Rotation is the key's rotation schedule, if any.
Rotation Rotation `json:"rotation,omitzero"`
// Policy names the request policy of the key (empty: its team's).
Policy string `json:"policy,omitempty"`
// Guardrails names guardrails the key adds to those of its org and
// team (it can add, never remove).
Guardrails []string `json:"guardrails,omitempty"`
}
Key is a virtual key. The secret itself is never stored, only its SHA-256.
type Lease ¶ added in v0.13.1
type Lease struct {
Pool, Owner, Addr string
// Epoch is the fencing token: it grows by one at every change of
// owner (or when an owner takes back a lease it let expire).
Epoch int64
// Live: the lease has not expired (by PostgreSQL's clock).
Live bool
// Yield: the owner asks to be replaced (it cannot read the pool's
// membership any more, #75); Quiet: this claim took such a lease, so
// the new owner must not decide for one lease duration.
Yield, Quiet bool
// Capacity and Reserved are what the owner published: the pool's C
// and the integer reservations (JSON) it decides with at this epoch
// (-1 and "": not published).
Capacity int
Reserved string
}
Lease is a pool's lease as PostgreSQL holds it.
type Memory ¶
type Memory struct {
MaxRecords int
// Fail, when set, is returned by every call (fault injection). Set it
// with SetFail.
Fail error
// contains filtered or unexported fields
}
Memory is an in-process Store. Usage records are bounded by MaxRecords (oldest dropped first).
func (*Memory) Allowlists ¶ added in v0.4.0
func (*Memory) AuditAddCheckpoint ¶ added in v0.5.0
func (m *Memory) AuditAddCheckpoint(_ context.Context, c AuditCheckpoint) error
func (*Memory) AuditAppend ¶ added in v0.5.0
func (m *Memory) AuditAppend(_ context.Context, e AuditEntry, hash AuditHasher) (AuditEntry, error)
func (*Memory) AuditCheckpoints ¶ added in v0.5.0
func (m *Memory) AuditCheckpoints(context.Context) ([]AuditCheckpoint, error)
func (*Memory) AuditPrune ¶ added in v0.5.0
func (*Memory) AuditRange ¶ added in v0.5.0
func (*Memory) CreateProject ¶ added in v0.2.0
func (*Memory) CreateUser ¶ added in v0.2.0
func (*Memory) CreateUserToken ¶ added in v0.2.0
func (*Memory) DeleteAudit ¶ added in v0.5.0
DeleteAudit removes one stored entry, bypassing everything: for tests that must show the verifier catches it.
func (*Memory) DisableOrg ¶ added in v0.2.0
func (*Memory) DisableProject ¶ added in v0.2.0
func (*Memory) DisableUser ¶ added in v0.2.0
func (*Memory) IncreaseBudget ¶ added in v0.2.0
func (*Memory) InsertRecords ¶
func (*Memory) ListProjects ¶ added in v0.2.0
func (*Memory) ListUserTokens ¶ added in v0.2.0
func (*Memory) MarkSoftAlert ¶ added in v0.2.0
func (*Memory) Records ¶ added in v0.2.0
Records returns a copy of the stored usage records (tests, trials).
func (*Memory) RevokeUserToken ¶ added in v0.2.0
func (*Memory) SCIMCreate ¶ added in v0.4.0
func (*Memory) SCIMDelete ¶ added in v0.4.0
func (*Memory) SCIMReplace ¶ added in v0.4.0
func (*Memory) SetAllowlist ¶ added in v0.4.0
func (*Memory) SetKeyRotation ¶ added in v0.4.0
func (*Memory) SetTeamModels ¶ added in v0.2.0
func (*Memory) SetTeamTools ¶ added in v0.8.0
func (*Memory) TamperAudit ¶ added in v0.5.0
func (m *Memory) TamperAudit(seq int64, f func(*AuditEntry))
TamperAudit rewrites one stored entry in place, bypassing the chain: for tests that must show the verifier catches it.
func (*Memory) TeamModels ¶ added in v0.2.0
type Org ¶ added in v0.2.0
type Org struct {
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
Disabled bool `json:"disabled"`
}
Org is an organisation (an institution), the top of the hierarchy.
type OutboxAlert ¶ added in v0.13.1
OutboxAlert is a claimed alert.
type Period ¶
type Period string
Period is a budget period, aligned on UTC calendar boundaries.
type PoolUsage ¶ added in v0.13.1
type PoolUsage struct {
Org string
Usage float64
InFlight int
// Age is how long ago it was written, by PostgreSQL's clock.
Age time.Duration
}
PoolUsage is one organisation's fair-share state in a pool, as the owner persisted it.
type Postgres ¶
type Postgres struct {
// contains filtered or unexported fields
}
Postgres is a Store on PostgreSQL through pgx.
func OpenPostgres ¶
OpenPostgres connects and creates or upgrades the schema. The error never contains the DSN.
func (*Postgres) Allowlists ¶ added in v0.4.0
func (*Postgres) AuditAddCheckpoint ¶ added in v0.5.0
func (p *Postgres) AuditAddCheckpoint(ctx context.Context, c AuditCheckpoint) error
func (*Postgres) AuditAppend ¶ added in v0.5.0
func (p *Postgres) AuditAppend(ctx context.Context, e AuditEntry, hash AuditHasher) (AuditEntry, error)
func (*Postgres) AuditCheckpoints ¶ added in v0.5.0
func (p *Postgres) AuditCheckpoints(ctx context.Context) ([]AuditCheckpoint, error)
func (*Postgres) AuditPrune ¶ added in v0.5.0
func (*Postgres) AuditRange ¶ added in v0.5.0
func (*Postgres) ClaimAlerts ¶ added in v0.13.1
func (*Postgres) ClaimLease ¶ added in v0.13.1
func (*Postgres) CreateProject ¶ added in v0.2.0
func (*Postgres) CreateTeam ¶
func (*Postgres) CreateUser ¶ added in v0.2.0
func (*Postgres) CreateUserToken ¶ added in v0.2.0
func (*Postgres) DeleteBudget ¶
func (*Postgres) DisableOrg ¶ added in v0.2.0
func (*Postgres) DisableProject ¶ added in v0.2.0
func (*Postgres) DisableTeam ¶
func (*Postgres) DisableUser ¶ added in v0.2.0
func (*Postgres) DropSealed ¶ added in v0.13.1
func (*Postgres) EnqueueAlert ¶ added in v0.13.1
func (*Postgres) FinishAlert ¶ added in v0.13.1
func (*Postgres) IncreaseBudget ¶ added in v0.2.0
func (*Postgres) InsertRecords ¶
InsertRecords writes a batch: COPY into a temporary table, then one INSERT that skips the records whose ID is already stored, so a batch replayed from the accounting spool is never billed twice.
func (*Postgres) ListProjects ¶ added in v0.2.0
func (*Postgres) ListUserTokens ¶ added in v0.2.0
func (*Postgres) ListenKeys ¶ added in v0.13.1
func (*Postgres) MarkSoftAlert ¶ added in v0.2.0
func (*Postgres) NotifyKeys ¶ added in v0.13.1
func (*Postgres) PurgeSealed ¶ added in v0.13.1
func (*Postgres) ReleaseLease ¶ added in v0.13.1
func (*Postgres) RevokeUserToken ¶ added in v0.2.0
func (*Postgres) SCIMCreate ¶ added in v0.4.0
func (*Postgres) SCIMDelete ¶ added in v0.4.0
func (*Postgres) SCIMReplace ¶ added in v0.4.0
func (*Postgres) SetAllowlist ¶ added in v0.4.0
func (*Postgres) SetKeyRotation ¶ added in v0.4.0
func (*Postgres) SetTeamModels ¶ added in v0.2.0
func (*Postgres) SetTeamTools ¶ added in v0.8.0
func (*Postgres) TakeSealed ¶ added in v0.13.1
func (*Postgres) TeamModels ¶ added in v0.2.0
func (*Postgres) UsageRecordIDs ¶ added in v0.10.0
UsageRecordIDs lists the record id of every usage row (empty for rows written before ids), for verifying that nothing was billed twice.
type Project ¶ added in v0.2.0
type Project struct {
Team string `json:"team"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
Disabled bool `json:"disabled"`
}
Project is a unit of work inside a team, with its own keys and budget.
type Record ¶
type Record struct {
// ID identifies the record: inserting a record whose ID is already
// stored does nothing, so the accounting spool can replay a batch that
// may have been written before a crash without billing it twice. Empty
// for records written before v0.10.0.
ID string
Time time.Time
Org string
Team string
Project string
KeyID string
// User is the gateway user the request is attributed to: the key's
// owner, or a JWT's username; "" for a service key. Never a token.
User string
// Lab is the team's lab at the time of the request ("" for none).
Lab string
Model string
Pool string
Upstream string
PromptTokens int64
CompletionTokens int64
CachedTokens int64
// Cost is in Unit: "" for GPU-hours (vLLM models), a currency code
// such as "USD" for an external provider. Costs of different units are
// never added together (budgets and reports split by unit).
Cost float64
Unit string
LatencyMS float64
Status int
Priority int
Borrowing bool
Stream bool
// Tags are the key's tags and the allow-listed request tags.
Tags []string
// Estimated is true when vLLM's usage report never arrived (client
// gone before the final usage chunk or before a non-streaming answer)
// and the tokens were estimated by the gateway.
Estimated bool
}
Record is one finished request.
type ReportRow ¶ added in v0.2.0
type ReportRow struct {
Org string `json:"org,omitempty"`
Lab string `json:"lab,omitempty"`
Team string `json:"team,omitempty"`
Project string `json:"project,omitempty"`
User string `json:"user,omitempty"`
Key string `json:"key,omitempty"`
Model string `json:"model,omitempty"`
Pool string `json:"pool,omitempty"`
Tag string `json:"tag,omitempty"`
Day string `json:"day,omitempty"`
Unit string `json:"-"`
Requests int64 `json:"requests"`
PromptTokens int64 `json:"prompt_tokens"`
CompletionTokens int64 `json:"completion_tokens"`
CachedTokens int64 `json:"cached_tokens"`
Cost float64 `json:"cost"`
GPUHours float64 `json:"gpu_hours"`
EstimatedRequests int64 `json:"estimated_requests"`
}
ReportRow is one group of a spend report. Only the dimensions asked for are filled. Every report is split by cost unit, whatever the dimensions: a row holds the costs of one unit only (Unit "" is GPU-hours), so GPU-hours and a currency are never summed. GPUHours repeats Cost for a GPU-hours row and is 0 otherwise (the v0.11 field, kept for clients that read it).
func Regroup ¶ added in v0.2.0
Regroup aggregates rows onto fewer dimensions, sorted, keeping the cost units apart.
func (ReportRow) MarshalJSON ¶ added in v0.12.0
MarshalJSON writes the unit by its name ("gpu_hours" for "").
type Role ¶ added in v0.2.0
type Role string
Role is an administrative role. Roles are bound to a user on a scope: the whole proxy, an organisation or a team. The set follows LiteLLM's access-control roles (proxy admin, proxy admin viewer, org admin, team admin, member) and Slurm's account coordinators, who manage the sub-accounts and users of the accounts they coordinate.
const ( // RoleProxyAdmin administers everything (like the master key). RoleProxyAdmin Role = "proxy_admin" // RoleProxyViewer reads everything and changes nothing. RoleProxyViewer Role = "proxy_viewer" // RoleOrgAdmin administers one organisation: its teams, projects, // keys, budgets and the roles of its members. RoleOrgAdmin Role = "org_admin" // RoleTeamAdmin administers one team: its projects, keys and members. RoleTeamAdmin Role = "team_admin" // RoleMember uses one team: creates, lists and revokes its own keys. RoleMember Role = "member" // RoleViewer reads one organisation or team: listings and spend. RoleViewer Role = "viewer" )
Roles, from the most to the least privileged on a given scope.
type Rotation ¶ added in v0.4.0
type Rotation struct {
Every Duration `json:"every,omitzero"`
Grace Duration `json:"grace_period,omitzero"`
Target string `json:"target,omitempty"`
}
Rotation is a scheduled rotation: every Every, the key gets a new secret, written to Target (a vault: secret reference); the replaced secret stays valid for Grace.
type SCIMDoc ¶ added in v0.4.0
type SCIMDoc struct {
Kind string // "User" or "Group"
ID string
Name string
Data []byte // the resource as JSON
Version string
}
SCIMDoc is one provisioned resource.
type ScopeKind ¶ added in v0.2.0
type ScopeKind string
ScopeKind is what a budget caps.
type SealedLimits ¶ added in v0.13.1
type SealedLimits struct{ Total, PerNet int }
SealedLimits bound what PutSealed stores: in all for the kind, and per client network (0: no bound).
type SpendRow ¶ added in v0.2.0
type SpendRow struct {
Org, Team, Project, KeyID, Model string
Tags []string
Cost float64
Unit string
}
SpendRow is the cost of one combination of attributes since a time, in one unit.
type Store ¶
type Store interface {
Ping(ctx context.Context) error
CreateOrg(ctx context.Context, name string) error
ListOrgs(ctx context.Context) ([]Org, error)
// DisableOrg disables an organisation, its teams and projects, and
// revokes all their keys.
DisableOrg(ctx context.Context, name string) error
// CreateTeam creates a team in an enabled organisation, re-enables a
// disabled team of the same organisation, adopts a team created
// without organisation (v0.1 databases), and returns ErrExists for an
// enabled team of the same organisation, ErrConflict for a team of
// another organisation, ErrNotFound for an unknown or disabled org.
CreateTeam(ctx context.Context, name, org string) error
GetTeam(ctx context.Context, name string) (Team, error)
// ListTeams lists the teams of an organisation, or all with org "".
ListTeams(ctx context.Context, org string) ([]Team, error)
// DisableTeam disables a team and its projects and revokes its keys.
DisableTeam(ctx context.Context, name string) error
CreateProject(ctx context.Context, team, name string) error
ListProjects(ctx context.Context, team string) ([]Project, error)
// DisableProject disables a project and revokes its keys.
DisableProject(ctx context.Context, team, name string) error
// CreateKey stores a key of an enabled team (and project, when set).
CreateKey(ctx context.Context, k Key) error
// ListKeys lists keys, filtered by team when team is not "".
ListKeys(ctx context.Context, team string) ([]Key, error)
GetKey(ctx context.Context, id string) (Key, error)
RevokeKey(ctx context.Context, id string) error
// RotateKey gives a key a new secret hash. The current one becomes
// the previous secret, valid until prevExpires (a zero or past time
// drops it at once), and RotatedAt becomes at (zero: none). With a
// non-zero expect, the key's current hash must be expect (ErrConflict
// otherwise), so that two replicas cannot rotate one key twice.
// ErrNotFound for an unknown or revoked key, ErrExists when the new
// hash is already another key's, or this key's current one.
RotateKey(ctx context.Context, id string, newHash, expect [32]byte, prevExpires, at time.Time) error
// SCIM provisioning documents (see SCIMDoc). SCIMCreate: ErrExists
// when the id, or a User's name without case, is taken. SCIMReplace:
// ErrConflict when the stored version is not ifVersion, ErrExists for a
// taken name. SCIMList filters by name without case when name != "".
SCIMCreate(ctx context.Context, d SCIMDoc) error
SCIMGet(ctx context.Context, kind, id string) (SCIMDoc, error)
SCIMReplace(ctx context.Context, d SCIMDoc, ifVersion string) error
SCIMDelete(ctx context.Context, kind, id string) error
SCIMList(ctx context.Context, kind, name string) ([]SCIMDoc, error)
// The audit log (see AuditEntry). AuditAppend assigns Seq and PrevHash
// under a lock held across replicas, calls hash, and stores the entry.
// AuditPrune deletes the entries through seq (the caller checks that a
// checkpoint anchors what remains).
AuditAppend(ctx context.Context, e AuditEntry, hash AuditHasher) (AuditEntry, error)
AuditRange(ctx context.Context, from int64, limit int) ([]AuditEntry, error)
AuditAddCheckpoint(ctx context.Context, c AuditCheckpoint) error
AuditCheckpoints(ctx context.Context) ([]AuditCheckpoint, error)
AuditPrune(ctx context.Context, through int64) (int64, error)
// SetKeyRotation sets a key's rotation schedule (every 0: none).
SetKeyRotation(ctx context.Context, id string, r Rotation) error
// DueKeys lists the keys, not revoked, whose scheduled rotation is due
// at now: RotatedAt (or CreatedAt) + Every <= now.
DueKeys(ctx context.Context, now time.Time) ([]Key, error)
// KeyByHash returns the key whose current or previous hash is hash,
// revoked or not, with its Org filled in, or ErrNotFound. The caller
// must still check it with Key.Matches.
KeyByHash(ctx context.Context, hash [32]byte) (Key, error)
CreateUser(ctx context.Context, name string) error
GetUser(ctx context.Context, name string) (User, error)
ListUsers(ctx context.Context) ([]User, error)
// DisableUser disables a user and revokes their admin tokens.
DisableUser(ctx context.Context, name string) error
CreateUserToken(ctx context.Context, t UserToken) error
ListUserTokens(ctx context.Context, user string) ([]UserToken, error)
RevokeUserToken(ctx context.Context, user, id string) error
// UserByTokenHash returns the token with that hash and its user, or
// ErrNotFound. The caller compares hashes in constant time and checks
// Revoked and Disabled.
UserByTokenHash(ctx context.Context, hash [32]byte) (User, UserToken, error)
// Grant stores a binding; ErrNotFound when the user, org or team does
// not exist, ErrExists when it is already granted.
Grant(ctx context.Context, b Binding) error
Ungrant(ctx context.Context, b Binding) error
// Bindings lists the bindings of a user, or all with user "".
Bindings(ctx context.Context, user string) ([]Binding, error)
// SetTeamModels replaces the list of model groups a team may use; an
// empty list allows every configured model.
SetTeamModels(ctx context.Context, team string, models []string) error
// TeamModels returns every team's non-empty model list.
TeamModels(ctx context.Context) (map[string][]string, error)
// SetTeamTools replaces the MCP tool patterns a team may use
// ("server.tool", "server.*" or "*"); an empty list allows none.
SetTeamTools(ctx context.Context, team string, tools []string) error
// TeamTools returns every team's non-empty tool list.
TeamTools(ctx context.Context) (map[string][]string, error)
// SetBudget creates or replaces a budget's period, maximum and soft
// limit; a temporary increase already granted is kept.
SetBudget(ctx context.Context, b Budget) error
// IncreaseBudget grants a temporary increase until expiry, replacing
// any earlier one; ErrNotFound when the budget does not exist.
IncreaseBudget(ctx context.Context, k BudgetKey, amount float64, expiry time.Time) error
DeleteBudget(ctx context.Context, k BudgetKey) error
Budgets(ctx context.Context) ([]Budget, error)
// MarkSoftAlert records that the soft-limit alert of a budget was sent
// for the period starting at period; it returns false when it already
// was, so that several gateways or restarts send it once.
MarkSoftAlert(ctx context.Context, k BudgetKey, period time.Time) (bool, error)
// SpendRows aggregates the cost of records at or after since by org,
// team, project, key, model and tag set.
SpendRows(ctx context.Context, since time.Time) ([]SpendRow, error)
InsertRecords(ctx context.Context, recs []Record) error
// SetAllowlist replaces the client networks allowed for an
// organisation, a team or a key; an empty list removes the allowlist.
SetAllowlist(ctx context.Context, a Allowlist) error
Allowlists(ctx context.Context) ([]Allowlist, error)
// Report aggregates the records in [from, to) by the given dimensions
// (see ReportDims), sorted.
Report(ctx context.Context, from, to time.Time, dims []string) ([]ReportRow, error)
Close()
}
Store is the persistence contract.
type Team ¶
type Team struct {
Name string `json:"name"`
Org string `json:"org"`
CreatedAt time.Time `json:"created_at"`
Disabled bool `json:"disabled"`
}
Team is a group (a lab) inside an organisation.