Documentation
¶
Index ¶
- func HashPassword(password string) (string, error)
- func ValidateTenantName(name string) error
- func ValidateTenantSlug(slug string) error
- type ControlPlane
- func (cp *ControlPlane) CleanupStalePending(maxAgeHours int) (int64, error)
- func (cp *ControlPlane) Close() error
- func (cp *ControlPlane) CreatePendingTenant(slug, name, email, plaintextPassword, plan, customerID string, maxUsers int, ...) error
- func (cp *ControlPlane) DataDir() string
- func (cp *ControlPlane) DeletePendingTenant(slug string) error
- func (cp *ControlPlane) DeleteTenant(slug string) error
- func (cp *ControlPlane) GetPendingTenant(slug string) (*PendingTenant, error)
- func (cp *ControlPlane) GetTenant(slug string) (*Tenant, error)
- func (cp *ControlPlane) GetTenantByCustomer(stripeCustomerID string) (*Tenant, error)
- func (cp *ControlPlane) IndexAPIKey(prefix, tenantSlug string) error
- func (cp *ControlPlane) ListTenants() ([]Tenant, error)
- func (cp *ControlPlane) LookupKeyTenant(prefix string) (string, error)
- func (cp *ControlPlane) MarkWebhookProcessed(eventID, eventType string) (bool, error)
- func (cp *ControlPlane) ProvisionTenant(slug, name, ownerEmail, ownerPasswordHash, plan, stripeCustomerID string, ...) (*Tenant, error)
- func (cp *ControlPlane) RemoveAPIKeyIndex(prefix string) error
- func (cp *ControlPlane) RemoveAllKeysByTenant(slug string) error
- func (cp *ControlPlane) SuspendTenant(slug string) error
- func (cp *ControlPlane) TenantDBPath(slug string) string
- func (cp *ControlPlane) TenantVaultPath(slug string) string
- func (cp *ControlPlane) UpdateStorageUsed(slug string, usedMB int64) error
- func (cp *ControlPlane) UpdateTenantPlan(slug, plan string, maxUsers int, storageLimitMB int64, subscriptionID string) error
- func (cp *ControlPlane) UpdateTenantStatus(slug, status string) error
- type PendingTenant
- type Tenant
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HashPassword ¶
func ValidateTenantName ¶
ValidateTenantName checks the human-readable display name. We don't allow control characters or newlines (which would corrupt logs and the nginx config comment block), and we cap length to keep the DB column reasonable.
func ValidateTenantSlug ¶
ValidateTenantSlug checks slug syntax. Exported so the CLI can call it before hashing the password and starting any side effects.
Types ¶
type ControlPlane ¶
type ControlPlane struct {
// contains filtered or unexported fields
}
ControlPlane manages the multi-tenant infrastructure. In the single-daemon architecture, it owns the control plane DB (tenant registry + api_keys_index) and the provisioning logic that creates per-tenant SQLite files + vault dirs. It does NOT manage per-tenant processes, nginx configs, or systemd units — all tenants share a single daemon process.
func New ¶
func New(dbPath, dataDir string) (*ControlPlane, error)
func (*ControlPlane) CleanupStalePending ¶
func (cp *ControlPlane) CleanupStalePending(maxAgeHours int) (int64, error)
CleanupStalePending removes pending rows older than maxAge. Run periodically to prevent unbounded growth from abandoned signups. Stripe checkout sessions expire after 24h by default, so anything older than that is dead and can be reaped.
func (*ControlPlane) Close ¶
func (cp *ControlPlane) Close() error
func (*ControlPlane) CreatePendingTenant ¶
func (cp *ControlPlane) CreatePendingTenant(slug, name, email, plaintextPassword, plan, customerID string, maxUsers int, storageLimitMB int64) error
CreatePendingTenant stores signup data before Stripe checkout. The plaintext password is hashed before storage — the bcrypt hash is what gets carried forward into the tenant's own DB on successful provisioning.
func (*ControlPlane) DataDir ¶
func (cp *ControlPlane) DataDir() string
DataDir returns the root data directory (e.g. /data/tenants).
func (*ControlPlane) DeletePendingTenant ¶
func (cp *ControlPlane) DeletePendingTenant(slug string) error
DeletePendingTenant removes a pending record (after successful provisioning).
func (*ControlPlane) DeleteTenant ¶
func (cp *ControlPlane) DeleteTenant(slug string) error
func (*ControlPlane) GetPendingTenant ¶
func (cp *ControlPlane) GetPendingTenant(slug string) (*PendingTenant, error)
GetPendingTenant retrieves pending signup data by slug.
func (*ControlPlane) GetTenantByCustomer ¶
func (cp *ControlPlane) GetTenantByCustomer(stripeCustomerID string) (*Tenant, error)
func (*ControlPlane) IndexAPIKey ¶
func (cp *ControlPlane) IndexAPIKey(prefix, tenantSlug string) error
IndexAPIKey registers a key prefix → tenant mapping. Called after every CreateAPIKey in a tenant DB so the daemon can route Bearer-token requests to the right tenant without scanning all DBs.
func (*ControlPlane) ListTenants ¶
func (cp *ControlPlane) ListTenants() ([]Tenant, error)
func (*ControlPlane) LookupKeyTenant ¶
func (cp *ControlPlane) LookupKeyTenant(prefix string) (string, error)
LookupKeyTenant resolves a Bearer token prefix to a tenant slug. Returns ("", nil) if the prefix is not in the index.
func (*ControlPlane) MarkWebhookProcessed ¶
func (cp *ControlPlane) MarkWebhookProcessed(eventID, eventType string) (bool, error)
MarkWebhookProcessed records an event ID as processed. Returns true if this was a fresh event (caller should run side effects), false if the event was already processed (caller should skip).
func (*ControlPlane) ProvisionTenant ¶
func (cp *ControlPlane) ProvisionTenant(slug, name, ownerEmail, ownerPasswordHash, plan, stripeCustomerID string, maxUsers int, storageLimitMB int64) (*Tenant, error)
ProvisionTenant creates a new tenant with isolated storage and database.
ownerPasswordHash MUST be a bcrypt hash, not plaintext. The HTTP signup path hashes the password in CreatePendingTenant; the CLI provision path (cmd/vbcloud) hashes immediately after collecting the plaintext from the operator. The control plane never stores or handles plaintext passwords past the entry point.
func (*ControlPlane) RemoveAPIKeyIndex ¶
func (cp *ControlPlane) RemoveAPIKeyIndex(prefix string) error
RemoveAPIKeyIndex deletes a single key from the index (e.g., on key revocation).
func (*ControlPlane) RemoveAllKeysByTenant ¶
func (cp *ControlPlane) RemoveAllKeysByTenant(slug string) error
RemoveAllKeysByTenant removes all index entries for a tenant (e.g., on deletion).
func (*ControlPlane) SuspendTenant ¶
func (cp *ControlPlane) SuspendTenant(slug string) error
func (*ControlPlane) TenantDBPath ¶
func (cp *ControlPlane) TenantDBPath(slug string) string
TenantDBPath returns the path to a tenant's SQLite database.
func (*ControlPlane) TenantVaultPath ¶
func (cp *ControlPlane) TenantVaultPath(slug string) string
TenantVaultPath returns the path to a tenant's vault directory.
func (*ControlPlane) UpdateStorageUsed ¶
func (cp *ControlPlane) UpdateStorageUsed(slug string, usedMB int64) error
func (*ControlPlane) UpdateTenantPlan ¶
func (*ControlPlane) UpdateTenantStatus ¶
func (cp *ControlPlane) UpdateTenantStatus(slug, status string) error
type PendingTenant ¶
type PendingTenant struct {
ID int64
Slug string
Name string
OwnerEmail string
OwnerPasswordHash string
Plan string
StripeCustomerID string
MaxUsers int
StorageLimitMB int64
}
PendingTenant is a tenant that has started signup but hasn't completed payment. Stored before Stripe checkout, completed when webhook fires.
OwnerPasswordHash holds a bcrypt hash of the owner's chosen password (not the plaintext). Storing plaintext here was a data-protection violation: the row might sit in the DB indefinitely (until the user pays or we GC it), and any DB dump would have leaked every signup attempt's plaintext password. The hash is forwarded directly into the tenant's own DB during ProvisionTenant — we never need to read the plaintext again.
type Tenant ¶
type Tenant struct {
ID int64 `json:"id"`
Slug string `json:"slug"`
Name string `json:"name"`
OwnerEmail string `json:"owner_email"`
Plan string `json:"plan"`
Status string `json:"status"` // "provisioning", "active", "suspended", "cancelled"
StripeCustomerID string `json:"stripe_customer_id"`
StripeSubscriptionID string `json:"stripe_subscription_id"`
MaxUsers int `json:"max_users"`
StorageLimitMB int64 `json:"storage_limit_mb"`
StorageUsedMB int64 `json:"storage_used_mb"`
Port int `json:"port"` // assigned port for this tenant's VaultBase process
DataDir string `json:"data_dir"` // /data/tenants/{slug}
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Tenant represents a provisioned customer.