Documentation
¶
Overview ¶
Package tenant stores the multi-tenant registry for a subrouter server. Each tenant owns an isolated account pool under <state-dir>/tenants/<id>/ that mirrors the single-tenant layout (codex/accounts/*.json, codex/claude/, sessions.json). Tenant keys look like srt_<32 hex>; only SHA-256 hashes are stored, so a key is shown once at creation and cannot be recovered later.
Index ¶
- Constants
- Variables
- func DeriveKey(secret []byte, namespace, externalID string) (string, error)
- func HashKey(key string) string
- func ValidExternalID(value string) bool
- func ValidKeyFormat(value string) bool
- type Capability
- type Key
- type Registry
- func (r *Registry) AcquireExclusiveUse(id string) (*UseLock, error)
- func (r *Registry) AcquireUse(id string) (*UseLock, error)
- func (r *Registry) Create(name string) (Tenant, string, error)
- func (r *Registry) CreateKey(tenantID string) (Tenant, string, error)
- func (r *Registry) DeleteRetired(id string) (bool, error)
- func (r *Registry) Dir(id string) string
- func (r *Registry) EnsureExternal(id, name, plaintextKey string) (Tenant, error)
- func (r *Registry) EnsureExternalRestricted(id, name, plaintextKey string, capabilities []Capability) (Tenant, error)
- func (r *Registry) EnsureLegacyCredentialCutoff(now time.Time, grace time.Duration) (time.Time, error)
- func (r *Registry) Find(ref string) (Tenant, bool, error)
- func (r *Registry) HasTenants() bool
- func (r *Registry) List() ([]Tenant, error)
- func (r *Registry) Path() string
- func (r *Registry) PendingDeletionIDs() ([]string, error)
- func (r *Registry) Resolve(key string) (Tenant, bool, error)
- func (r *Registry) ResolveCredential(key string) (Tenant, Key, bool, error)
- func (r *Registry) ResolveFresh(key string) (Tenant, bool, error)
- func (r *Registry) ResolveFreshCredential(key string) (Tenant, Key, bool, error)
- func (r *Registry) RetireExternal(id string) (bool, error)
- func (r *Registry) RevokeKey(tenantID, keyRef string) (int, error)
- func (r *Registry) TenantsDir() string
- func (r *Registry) TryAcquireExclusiveUse(id string) (*UseLock, bool, error)
- type Tenant
- type UseLock
Constants ¶
const KeyPrefix = "srt_"
const MaxLegacyCredentialGrace = 90 * 24 * time.Hour
Variables ¶
var ErrTenantRetired = errors.New("tenant is retired")
Functions ¶
func DeriveKey ¶ added in v0.1.52
DeriveKey deterministically maps an external tenant identity to a valid tenant key. The secret stays on the server; only the derived srt_ key is returned to the authenticated client.
func ValidExternalID ¶ added in v0.1.52
ValidExternalID permits lowercase identity-provider IDs as directory names while rejecting separators, traversal, and case-folding collisions.
func ValidKeyFormat ¶
ValidKeyFormat reports whether value is shaped like a tenant key (srt_ followed by 32 lowercase hex chars).
Types ¶
type Capability ¶ added in v0.1.55
type Capability string
const ( CapabilityUse Capability = "use" CapabilityManageAccounts Capability = "manage_accounts" )
type Key ¶
type Key struct {
Hash string `json:"hash"`
Prefix string `json:"prefix"`
CreatedAt time.Time `json:"createdAt"`
Restricted bool `json:"restricted,omitempty"`
Capabilities []Capability `json:"capabilities,omitempty"`
}
func (Key) Allows ¶ added in v0.1.55
func (k Key) Allows(capability Capability) bool
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry reads and writes tenants.json under a server state dir. Reads are cached on file modtime+size so the per-request key resolution is one stat.
func NewRegistry ¶
func (*Registry) AcquireExclusiveUse ¶ added in v0.1.53
AcquireExclusiveUse waits until every active request has released its shared tenant-use lock, then holds the deletion lock.
func (*Registry) AcquireUse ¶ added in v0.1.53
func (*Registry) Create ¶
Create registers a new tenant, provisions its state dir, and returns the tenant plus its first key in plaintext (the only time it is available).
func (*Registry) CreateKey ¶
CreateKey mints an additional key for an existing tenant and returns it in plaintext.
func (*Registry) DeleteRetired ¶ added in v0.1.53
DeleteRetired permanently removes a retired tenant's credential-bearing state. A durable tombstone prevents a still-valid Stack token from recreating the tenant in the interval before the owning identity is deleted. The caller must hold the tenant's exclusive use lock.
func (*Registry) EnsureExternal ¶ added in v0.1.52
EnsureExternal creates or updates the tenant owned by an external identity provider. plaintextKey must be a deterministic key derived by the caller, so repeated logins return the same client configuration without accumulating registry keys.
func (*Registry) EnsureExternalRestricted ¶ added in v0.1.55
func (r *Registry) EnsureExternalRestricted( id, name, plaintextKey string, capabilities []Capability, ) (Tenant, error)
EnsureExternalRestricted creates or updates an externally owned tenant key whose authority is limited to the supplied capabilities.
func (*Registry) EnsureLegacyCredentialCutoff ¶ added in v0.1.56
func (r *Registry) EnsureLegacyCredentialCutoff( now time.Time, grace time.Duration, ) (time.Time, error)
EnsureLegacyCredentialCutoff creates one durable deadline for credentials issued by the pre-broker Stack exchange. Repeated starts and overlapping worker generations read the original deadline instead of extending it.
func (*Registry) HasTenants ¶
HasTenants reports whether at least one tenant exists.
func (*Registry) PendingDeletionIDs ¶ added in v0.1.53
PendingDeletionIDs lists retired registry entries and crash-recovery markers that need credential-state deletion after active requests drain.
func (*Registry) Resolve ¶
Resolve maps a plaintext tenant key to its tenant. A revoked or unknown key returns ok=false.
func (*Registry) ResolveCredential ¶ added in v0.1.55
func (*Registry) ResolveFresh ¶ added in v0.1.53
ResolveFresh bypasses the metadata cache after a request has acquired its shared use lock. This closes the cross-process race with tenant retirement.
func (*Registry) ResolveFreshCredential ¶ added in v0.1.55
func (*Registry) RetireExternal ¶ added in v0.1.53
RetireExternal revokes every key and records deletion intent under one interprocess registry lock. This prevents a first exchange from creating an absent tenant between retirement and deletion. It is idempotent while active requests drain.
func (*Registry) RevokeKey ¶
RevokeKey removes the key whose display prefix exactly matches keyRef, or whose hash matches when keyRef is a full plaintext key, and returns how many keys were revoked. Exact matching only: a loose prefix like "srt_" must not wipe every key on the tenant.