controlplane

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 15, 2026 License: AGPL-3.0-or-later Imports: 12 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HashPassword

func HashPassword(password string) (string, error)

func ValidateTenantName

func ValidateTenantName(name string) error

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

func ValidateTenantSlug(slug string) error

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) GetTenant

func (cp *ControlPlane) GetTenant(slug string) (*Tenant, error)

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 (cp *ControlPlane) UpdateTenantPlan(slug, plan string, maxUsers int, storageLimitMB int64, subscriptionID string) error

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.

Jump to

Keyboard shortcuts

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