Documentation
¶
Overview ¶
tenant_context.go is the ONE source of truth for the tenant-hierarchy request scope (org > app > project, plus tenant/actor/env). The router filter (routers.TenantContextFilter) copies the gateway-injected X-*-ID headers onto the beego request context under these keys; every consumer (controllers) reads them back through the getters here. Keeping the keys and their accessors in one shared place — imported by both routers and controllers — means no consumer re-derives a key string: the scope is a value, not a place.
Index ¶
- Constants
- func AddAgentBinding(binding *AgentBinding) (bool, error)
- func AddAsset(asset *Asset) (bool, error)
- func AddMachine(machine *Machine) (bool, error)
- func AddNodePool(pool *NodePool) (bool, error)
- func AddPlan(plan *Plan) (bool, error)
- func AddProvider(provider *Provider) (bool, error)
- func AddRecord(record *Record) bool
- func AddSession(session *Session) (bool, error)
- func AddVolume(volume *Volume) (bool, error)
- func AdvanceCostCursor(owner, provider, month string, currentMTDCents int64) (int64, error)
- func AttachVolumeCloud(owner string, providerName string, volumeCloudID string, machineCloudID string) error
- func ClaimBillingUnit(unit string, now time.Time) bool
- func ClaimMeterHour(now time.Time) bool
- func CloseDBSession(id string, code int, msg string) error
- func CloseSession(id string, code int, msg string) error
- func CommitRecord(record *Record) (bool, error)
- func DBPath(root, owner string) string
- func DeleteAgentBinding(binding *AgentBinding) (bool, error)
- func DeleteAsset(asset *Asset) (bool, error)
- func DeleteMachine(machine *Machine) (bool, error)
- func DeleteNodePool(pool *NodePool) (bool, error)
- func DeleteNodePoolCloud(pool *NodePool) (bool, error)
- func DeletePlan(plan *Plan) (bool, error)
- func DeleteProvider(provider *Provider) (bool, error)
- func DeleteRecord(record *Record) (bool, error)
- func DeleteSession(session *Session) (bool, error)
- func DeleteSessionById(id string) (bool, error)
- func DeleteVolume(volume *Volume) (bool, error)
- func DeleteVolumeCloud(owner string, providerName string, volumeCloudID string) error
- func DetachVolumeCloud(owner string, providerName string, volumeCloudID string) error
- func DisconnectFleetWorker(owner, name string) (bool, error)
- func EngineFor(owner string) (*relational.Engine, error)
- func GetAllNodePools(pools *[]*NodePool) error
- func GetAssetCount(owner, field, value string) (int64, error)
- func GetBearerUser(authHeader string) *iam.User
- func GetKubernetesNodesCloud(owner string) ([]*service.Machine, error)
- func GetMachineCount(owner, field, value string) (int64, error)
- func GetNodePoolCount(owner, field, value string) (int64, error)
- func GetProviderCount(owner, field, value string) (int64, error)
- func GetRecordCount(owner, field, value string) (int64, error)
- func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *relational.Session
- func GetSessionCount(owner, status, field, value string) (int64, error)
- func GetTenantActorID(ctx *context.Context) string
- func GetTenantAppID(ctx *context.Context) string
- func GetTenantContextValue(ctx *context.Context, key string) string
- func GetTenantEnv(ctx *context.Context) string
- func GetTenantOrgID(ctx *context.Context) string
- func GetTenantProjectID(ctx *context.Context) string
- func InitAdapter()
- func InitConfig()
- func InitStore()
- func IsBillingOwner() bool
- func QueryRecord(id string) (string, error)
- func ResizeVolumeCloud(owner string, providerName string, volumeCloudID string, sizeGB int) error
- func SeedDefaultPlans(owner string) error
- func SetTenantContextValue(ctx *context.Context, key, value string)
- func Shared() *relational.Engine
- func SyncMachinesCloud(owner string) (bool, error)
- func SyncNodePoolsCloud(owner string) (bool, error)
- func UnbindAgent(machineId string) (bool, error)
- func UpdateAgentBinding(binding *AgentBinding) (bool, error)
- func UpdateAsset(id string, asset *Asset) (bool, error)
- func UpdateMachine(id string, machine *Machine) (bool, error)
- func UpdateNodePool(id string, pool *NodePool) (bool, error)
- func UpdatePlan(owner string, name string, plan *Plan) (bool, error)
- func UpdateProvider(id string, provider *Provider) (bool, error)
- func UpdateRecord(id string, record *Record) (bool, error)
- func UpdateSession(id string, session *Session, columns ...string) (bool, error)
- func UpdateVolume(owner string, name string, volume *Volume) (bool, error)
- func WriteCloseMessage(session *guacamole.Session, mode string, code int, msg string)
- type Adapter
- type AgentBinding
- type Asset
- func GetAsset(id string) (*Asset, error)
- func GetAssets(owner string) ([]*Asset, error)
- func GetMaskedAsset(asset *Asset, errs ...error) (*Asset, error)
- func GetMaskedAssets(assets []*Asset, errs ...error) ([]*Asset, error)
- func GetPaginationAssets(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Asset, error)
- type BillingLease
- type CostCursor
- type FleetWorker
- type Machine
- func CreateMachineCloud(owner string, providerName string, spec *service.CreateMachineSpec) (*Machine, error)
- func GetMachine(id string) (*Machine, error)
- func GetMachines(owner string) ([]*Machine, error)
- func GetMaskedMachine(machine *Machine, errs ...error) (*Machine, error)
- func GetMaskedMachines(machines []*Machine, errs ...error) ([]*Machine, error)
- func GetPaginationMachines(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Machine, error)
- type MeterLease
- type MigrationReport
- type NodePool
- func CreateNodePoolCloud(owner, providerName, clusterID string, spec *service.CreateNodePoolSpec) (*NodePool, error)
- func GetNodePool(id string) (*NodePool, error)
- func GetNodePools(owner string) ([]*NodePool, error)
- func GetPaginationNodePools(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*NodePool, error)
- func ScaleNodePoolCloud(owner, providerName, clusterID, poolID string, count int) (*NodePool, error)
- type Param
- type Patch
- type Plan
- type Provider
- func GetAllActiveCloudProviders() ([]*Provider, error)
- func GetMaskedProvider(provider *Provider, errs ...error) (*Provider, error)
- func GetMaskedProviders(providers []*Provider, errs ...error) ([]*Provider, error)
- func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Provider, error)
- func GetProvider(id string) (*Provider, error)
- func GetProviders(owner string) ([]*Provider, error)
- type Record
- type RemoteApp
- type Response
- type Service
- type Session
- func CreateSession(session *Session, machineId string, mode string) (*Session, error)
- func GetConnSession(id string) (*Session, error)
- func GetPaginationSessions(owner, status string, offset, limit int, ...) ([]*Session, error)
- func GetSessions(owner string) ([]*Session, error)
- func GetSessionsByStatus(statuses []string) ([]*Session, error)
- type StorageBackend
- type Volume
- type WhitelabelConfig
Constants ¶
const ( AgentBindingPending = "Pending" AgentBindingBound = "Bound" AgentBindingError = "Error" )
Agent binding lifecycle states. Honest — each reflects a real, observable condition, never an optimistic guess:
- Pending: binding recorded; the machine's @hanzo/bot runtime has not yet been confirmed running (machine still provisioning, or bot cloud-init not yet observed on the instance tags).
- Bound: the machine is Active AND is tagged as running the @hanzo/bot runtime for this agent (the launch cloud-init path stamps a `hanzo-bot:<agentName>` provider tag).
- Unbound: the binding was explicitly deleted; retained transiently only as a return value, never persisted (DELETE removes the row).
- Error: the machine is in a terminal/failed cloud state, so the runtime cannot be running.
const ( FleetWorkerBYOHardware = "byo-hardware" FleetWorkerValidator = "validator" )
Fleet worker kinds — the BILLING LINEAGE of a connected compute source, resolved once at connect. It is exactly the fleet-billing tier the source belongs to:
- FleetWorkerBYOHardware: a bare-metal / GPU / device box NOT on a cloud → flat $1/mo per connected device (DeviceCount), armed on connect and stopped on disconnect.
- FleetWorkerValidator: a box whose validator address is in the hanzo.network mainnet validator set → FREE / native (it already earns validator economics and secures the chain), so it is never metered.
A BYOC cloud ACCOUNT is a Provider, not a worker — it is billed at 1% of its cloud spend by the daily cost collector — so the worker registry is exactly the non-cloud tiers (b) and (c). One source, one kind, one rate.
const ( FleetWorkerConnected = "connected" FleetWorkerDisconnected = "disconnected" )
Worker connection states.
const ( NoConnect = "no_connect" Connecting = "connecting" Connected = "connected" Disconnected = "disconnected" )
const ( TenantContextOrgIDKey = "tenant.orgId" TenantContextAppIDKey = "tenant.appId" TenantContextProjectIDKey = "tenant.projectId" TenantContextTenantIDKey = "tenant.tenantId" TenantContextActorIDKey = "tenant.actorId" TenantContextEnvKey = "tenant.env" )
Tenant-scope keys under which the filter stows each header on the request context. Exported so the single writer (the routers filter) and every reader (controllers) share the exact same key — never a duplicated literal.
const BotRuntimeTag = "hanzo-bot"
BotRuntimeTag is the provider tag that marks a machine as running the @hanzo/bot runtime for a given agent. The launch cloud-init path is the single writer; reconcile is the single reader. The value is the agent name so one machine hosts exactly one agent's bot (the machine IS the bot's identity).
Variables ¶
This section is empty.
Functions ¶
func AddAgentBinding ¶
func AddAgentBinding(binding *AgentBinding) (bool, error)
AddAgentBinding inserts a binding. Callers set identity + timestamps first.
func AddMachine ¶
func AddNodePool ¶
func AddProvider ¶
func AddSession ¶
func AdvanceCostCursor ¶
AdvanceCostCursor advances the (owner, provider, month) watermark to the account's current month-to-date spend and returns the newly-billable spend INCREMENT (the month-to-date spend that had not yet been 1%-billed). It advances the watermark BEFORE the caller debits, so a debit failure UNDER-bills that increment — the safe direction for a paid product (a missed fee is reconcilable; a double debit is not). A zero/negative delta (no new spend, or a spend that only decreased via credits) advances nothing and returns 0.
func AttachVolumeCloud ¶
func ClaimBillingUnit ¶
ClaimBillingUnit claims a billing unit cluster-wide for exactly-once metering. It returns true to EXACTLY ONE caller for a given unit; every other replica — and any replay of the same unit — gets false and must skip. Best-effort prune of stale rows runs only for the winner, so it costs nothing on the hot skip path.
func ClaimMeterHour ¶
ClaimMeterHour attempts to claim the hourly metering sweep for the wall-clock hour containing now, cluster-wide. It returns true to EXACTLY ONE caller per hour (the elected owner whose Insert wins the Hour primary key); every other replica — a non-owner, or any re-entry within the same hour after a restart — gets false and must skip the sweep.
Exactly-once is the shared claimLease primitive: the single-writer gate (only the elected `_global` owner claims), a hydrate of the prior owner's leases before the insert, and a synchronous ship of the winning claim before the caller debits. Any failure (not owner, hydrate/ship failed, PK lost, DB error) yields false: fail SAFE for a paid product means NOT sweeping (a missed hour is reconciled; a duplicate debit is not). Best-effort prune of stale rows runs only for the winner, so it costs nothing on the hot skip path.
func CommitRecord ¶
func DBPath ¶
DBPath is the HIP-0302 per-org SQLite path for a visor org DB. It mirrors the layout hanzo/cloud uses (orgs/<org>/<service>.db) so every Hanzo service that moves onto Base shares one on-disk convention.
DBPath("/data", "acme") -> /data/orgs/acme/visor.db
The empty owner (catalog / cluster-global rows that no org owns) routes to the _global sentinel, matching hanzo/cloud's fail-loud migration bucket.
func DeleteAgentBinding ¶
func DeleteAgentBinding(binding *AgentBinding) (bool, error)
DeleteAgentBinding removes a binding by PK (unbind). It does NOT touch the machine — unbinding severs the agent↔machine record; tearing down the machine is a separate DeleteMachine call so the two lifecycles stay orthogonal.
func DeleteAsset ¶
func DeleteMachine ¶
func DeleteNodePool ¶
func DeleteNodePoolCloud ¶
DeleteNodePoolCloud deletes a node pool from its cloud provider (DOKS) and, only once the provider confirms the pool is gone, removes the DB row. A pool with no cloud linkage is a DB-only record and is removed directly. A provider error other than 404 (e.g. a transient 422 during provisioning) is propagated and the DB row is left intact so a retry can reconcile once the pool settles.
func DeletePlan ¶
func DeleteProvider ¶
func DeleteRecord ¶
func DeleteSession ¶
func DeleteSessionById ¶
func DeleteVolume ¶
func DeleteVolumeCloud ¶
func DetachVolumeCloud ¶
func DisconnectFleetWorker ¶
DisconnectFleetWorker marks a worker disconnected (stopping its device fee), returning whether a connected worker was present. The row is retained (not deleted) so its history and last DeviceCount survive for the closing month's bill; the meter simply stops counting it.
func EngineFor ¶
func EngineFor(owner string) (*relational.Engine, error)
EngineFor returns the storage engine that serves owner under the configured backend. This is the single entry point backend-agnostic code MUST use so a query works unchanged on either backend.
func GetAllNodePools ¶
GetAllNodePools fetches all Active node pools across ALL owners for billing reporting. Under Postgres this is one query over the single engine; under Base it unions the per-org SQLite DBs (allEngines), since no single table spans tenants.
func GetAssetCount ¶
func GetBearerUser ¶
GetBearerUser validates an "Authorization: Bearer <IAM JWT>" header and returns the authenticated user, or nil. iam.ParseJwtToken verifies the token SIGNATURE (jwt.ParseWithClaims + x509), so a forged/tampered token is rejected.
Signature alone is NOT sufficient: Hanzo IAM publishes ONE shared JWKS holding every brand's cert (hanzo/lux/zoo/pars/...), so a validly-signed token from any brand — including public self-service signups — passes the signature check. We therefore bind the token to THIS deployment by BRAND (issuer), not by a single org:
- owner (org) must be non-empty (an empty-org token can't be scoped), and
- issuer must match the configured brand issuer(s) in iamIssuer, so a sibling brand's token (lux.id/zoo.id/pars.id) is rejected even though its signature verifies.
EVERY org within this brand is accepted — the resell compute surface is multi-tenant (org "hanzo", "maxpower", and every self-service customer org). Each request is org-scoped downstream: resolveComputeOrg pins org = user.Owner (no ?owner override for real users) and Casbin enforces subOwner==objOwner, so one org can never reach another's machines. This is how API/console callers authenticate as a user (org = user.Owner) from a forwarded short-lived Bearer, without a browser cookie session.
func GetKubernetesNodesCloud ¶
GetKubernetesNodesCloud returns DOKS worker nodes — as service.Machines — for every active BYOC DigitalOcean provider that names a cluster (Provider.ClusterID). This is the Provider-record cluster→org association; the house-account tag association lives in service.ListOrgKubernetesNodes and the controller unions both, so a cluster discovered by either path surfaces its nodes (deduped by droplet id) exactly once. The DO provider selection mirrors SyncNodePoolsCloud (Type=="DigitalOcean" && ClusterID!=""), so nodes come from the same clusters visor already reconciles pools for.
func GetMachineCount ¶
func GetNodePoolCount ¶
func GetProviderCount ¶
func GetRecordCount ¶
func GetSession ¶
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *relational.Session
func GetSessionCount ¶
func GetTenantActorID ¶
GetTenantActorID returns the request's acting principal scope (or "").
func GetTenantAppID ¶
GetTenantAppID returns the request's optional app scope beneath org (or "").
func GetTenantContextValue ¶
GetTenantContextValue reads a scope value back, trimmed; "" when unset or when ctx is nil. This is the ONE read-back; the typed getters below are thin adapters.
func GetTenantEnv ¶
GetTenantEnv returns the request's environment scope (or "").
func GetTenantOrgID ¶
GetTenantOrgID returns the request's owning org scope (or "").
func GetTenantProjectID ¶
GetTenantProjectID returns the request's optional project scope beneath app (or "").
func InitAdapter ¶
func InitAdapter()
func InitConfig ¶
func InitConfig()
func InitStore ¶
func InitStore()
InitStore installs the pgStore engine provider for the Postgres backend. Base mode is wired directly in InitAdapter (it never opens a Postgres adapter), so this runs only on the opt-in Postgres path.
func IsBillingOwner ¶
func IsBillingOwner() bool
IsBillingOwner is the exported ownership gate the metering ticker checks before it arms an hourly sweep, so non-owner replicas never even enumerate machines or call commerce. It is an optimization layered over the authoritative per-claim gate inside ClaimMeterHour / ClaimBillingUnit — both consult the SAME predicate, so the ticker gate can never admit a claim the lease path would refuse.
func QueryRecord ¶
func ResizeVolumeCloud ¶
func SeedDefaultPlans ¶
SeedDefaultPlans inserts default plans if none exist for the given owner.
func SetTenantContextValue ¶
SetTenantContextValue stows a non-empty scope value on the request context. Empty values are skipped so an absent header leaves the key unset and every getter falls through to "" — the caller decides whether a missing scope is fatal (org) or optional (app/project).
func Shared ¶
func Shared() *relational.Engine
Shared returns the engine holding the cross-pod shared tables (the Plan catalog and the MeterLease coordination lease). Postgres under both backends.
func SyncMachinesCloud ¶
func SyncNodePoolsCloud ¶
SyncNodePoolsCloud fetches node pools from DO for all active DigitalOcean providers with a ClusterID and upserts them into the DB.
func UnbindAgent ¶
UnbindAgent removes the binding for a machine. Returns whether a binding was present. Orthogonal to the machine lifecycle — it never deletes the machine.
func UpdateAgentBinding ¶
func UpdateAgentBinding(binding *AgentBinding) (bool, error)
UpdateAgentBinding writes all columns of an existing binding by PK.
func UpdateSession ¶
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter represents the database adapter for policy storage.
func NewAdapter ¶
NewAdapter is the constructor for Adapter.
type AgentBinding ¶
type AgentBinding struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
// Project is the attribution dimension alongside Owner: the project WITHIN the
// org this agent binding belongs to. Additive, Sync2-safe, defaults to "".
Project string `xorm:"varchar(100)" json:"project"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
// Bound cloud Agent identity. `Org` is the cloud/IAM organization that owns
// the Agent (`<org>-<agent>` service-account naming); `AgentName` is the
// Agent's name in the cloud `/v1/agents` registry.
Org string `xorm:"varchar(100)" json:"org"`
AgentName string `xorm:"varchar(100)" json:"agentName"`
// MachineId is the machine's id (`owner/machine-name`), duplicated from the
// PK for a stable JSON field the operator can echo without re-splitting.
MachineId string `xorm:"varchar(200)" json:"machineId"`
// Provider + PublicIp are denormalized snapshots of the bound machine for
// quick status reads without a second machine lookup.
Provider string `xorm:"varchar(100)" json:"provider"`
PublicIp string `xorm:"varchar(100)" json:"publicIp"`
// BotVersion is the @hanzo/bot npm version the runtime targets. Empty means
// "track the machine's launch default" (the cloud-init installs `@hanzo/bot`
// latest at provision time); a pinned semver is recorded verbatim.
BotVersion string `xorm:"varchar(100)" json:"botVersion"`
// Status is the reconciled lifecycle state (Pending/Bound/Error). Message
// carries the human-readable reason behind the current Status.
Status string `xorm:"varchar(100)" json:"status"`
Message string `xorm:"varchar(500)" json:"message"`
}
AgentBinding records that a machine runs the @hanzo/bot runtime for a specific cloud Agent. It is the vm half of the Bot lifecycle: a Bot = Agent (execution_mode=long-running) + a bound compute running @hanzo/bot. The operator's AgentDeployment controller drives this over `POST /v1/machines/:id/bind-agent`.
The machine is the identity: PK is (Owner, Name) where Name == the machine id (`owner/machine-name`). One machine hosts one agent's bot runtime, so the binding is 1:1 with the machine — re-binding a different agent replaces the prior binding rather than layering.
func BindAgent ¶
func BindAgent(machineId string, org string, agentName string, botVersion string) (*AgentBinding, error)
BindAgent provisions/marks a machine as running the @hanzo/bot runtime for a cloud Agent. It is idempotent: binding a machine that is already bound to the same agent re-reconciles status; binding it to a DIFFERENT agent replaces the prior binding (the machine hosts exactly one agent's bot). The returned binding carries the freshly reconciled honest Status.
It does not itself install the runtime — the runtime is installed by the machine's launch cloud-init, which the operator drives via a machine launch with a `hanzo-bot:<agent>` tag before calling this. BindAgent records the agent↔machine relation and reports the observed convergence state.
func GetAgentBindings ¶
func GetAgentBindings(owner string) ([]*AgentBinding, error)
GetAgentBindings lists every binding owned by `owner`, newest first.
func ReconcileAgentBinding ¶
func ReconcileAgentBinding(machineId string) (*AgentBinding, error)
ReconcileAgentBinding re-derives and persists the honest Status of an existing binding against the live machine state. Used by the GET path so a read always reflects current convergence, and returns nil when the machine has no binding.
type Asset ¶
type Asset struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
Tag string `xorm:"varchar(100)" json:"tag"`
MachineName string `xorm:"varchar(100)" json:"machineName"`
Os string `xorm:"varchar(100)" json:"os"`
PublicIp string `xorm:"varchar(100)" json:"publicIp"`
PrivateIp string `xorm:"varchar(100)" json:"privateIp"`
Size string `xorm:"varchar(100)" json:"size"`
CpuSize string `xorm:"varchar(100)" json:"cpuSize"`
MemSize string `xorm:"varchar(100)" json:"memSize"`
RemoteProtocol string `xorm:"varchar(100)" json:"remoteProtocol"`
RemotePort int `json:"remotePort"`
RemoteUsername string `xorm:"varchar(100)" json:"remoteUsername"`
RemotePassword string `xorm:"varchar(100)" json:"remotePassword"`
AutoQuery bool `json:"autoQuery"`
IsPermanent bool `json:"isPermanent"`
Language string `xorm:"varchar(100)" json:"language"`
EnableRemoteApp bool `json:"enableRemoteApp"`
RemoteApps []*RemoteApp `json:"remoteApps"`
Services []*Service `json:"services"`
Patches []*Patch `json:"patches"`
}
type BillingLease ¶
type BillingLease struct {
Unit string `xorm:"varchar(128) notnull pk" json:"unit"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
}
BillingLease is the generic single-flight lease for a billable UNIT — a daily BYOC-cost line ("byoc:<owner>:<provider>:<YYYYMMDD>") or a monthly per-device line ("device:<owner>:<worker>:<YYYYMM>"). It is the money-safety twin of MeterLease: visor runs replicas: 2 with no leader election and commerce does NOT dedup the withdraw on requestId, so without a cluster-wide claim BOTH replicas would meter the same unit and double-bill it.
It is a SEPARATE table from MeterLease on purpose: the hourly compute sweep keeps using MeterLease unchanged, so a rolling deploy of this change can never make one replica claim an hour in `meter_lease` while another claims the same hour in a renamed column — which would double-bill every hour spanning the rollout. Unit is varchar(128) (MeterLease.Hour is varchar(12)); the daily/monthly keys do not fit the hour column, which is the other reason for a distinct table.
type CostCursor ¶
type CostCursor struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Provider string `xorm:"varchar(100) notnull pk" json:"provider"`
Month string `xorm:"varchar(6) notnull pk" json:"month"` // UTC "YYYYMM"
BilledSpendCents int64 `json:"billedSpendCents"` // account MTD spend already 1%-billed
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
}
CostCursor is the per-account watermark for BYOC cost billing (fleet tier a). It records how much of a BYOC provider's month-to-date cloud spend has ALREADY had the 1% fee applied, so each daily collector run meters only the INCREMENT since the last run — totaling 1% of the month's spend, idempotently. Month rollover is implicit in the PK: a new month starts a fresh cursor at zero, so the first run of a month bills 1% of that month's spend-to-date and no more.
The daily BillingLease serializes the read-modify-write across replicas (only the lease winner advances the cursor), so this row only needs to persist the advancing watermark — no compare-and-set is required.
type FleetWorker ¶
type FleetWorker struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
// Project is the attribution dimension alongside Owner (empty == default project).
Project string `xorm:"varchar(100)" json:"project"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
Kind string `xorm:"varchar(50)" json:"kind"` // FleetWorkerBYOHardware | FleetWorkerValidator
DeviceCount int `json:"deviceCount"` // GPUs/devices → the $1/mo multiplier (tier b)
GpuModel string `xorm:"varchar(100)" json:"gpuModel"` // informational (e.g. "H100")
// ValidatorAddress is the box's hanzo.network validator address; when Kind is
// FleetWorkerValidator it is matched against the on-chain validator set to grant
// the free-tier exemption. Empty for a plain BYO-hardware box.
ValidatorAddress string `xorm:"varchar(100)" json:"validatorAddress"`
State string `xorm:"varchar(50)" json:"state"` // FleetWorkerConnected | FleetWorkerDisconnected
LastSeen string `xorm:"varchar(100)" json:"lastSeen"`
}
FleetWorker is a BYO compute box connected to the Hanzo cloud fleet (the `hanzo gpu connect` / desktop-link path). It is keyed by (Owner, Name) — Owner is the org boundary, Name a stable per-box id (hostname/uuid) — with Project as the second attribution dimension so device billing is per org+project. The device meter reads DeviceCount while State==connected; the validator exemption reads Kind + ValidatorAddress.
func ConnectFleetWorker ¶
func ConnectFleetWorker(worker *FleetWorker) (*FleetWorker, error)
ConnectFleetWorker upserts a worker on connect (arming its billing), idempotent on (Owner, Name): a reconnect refreshes DeviceCount/Kind/LastSeen and flips State back to connected while preserving CreatedTime. It never fabricates identity — Owner/Name/Kind/DeviceCount are supplied by the caller (the connect handler, org-scoped and validated) and only timestamps + State are stamped here.
func GetConnectedFleetWorkers ¶
func GetConnectedFleetWorkers() ([]*FleetWorker, error)
GetConnectedFleetWorkers returns every CONNECTED worker across ALL orgs — the set the monthly device meter bills. Like the running-machine sweep, it lists cross-tenant and recovers the org from each row, so one pass meters every org's connected devices. A disconnected worker is excluded (device fee stops on disconnect).
func GetFleetWorkers ¶
func GetFleetWorkers(owner string) ([]*FleetWorker, error)
GetFleetWorkers lists an org's workers, newest first.
func (*FleetWorker) GetId ¶
func (w *FleetWorker) GetId() string
type Machine ¶
type Machine struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
// Project is the second attribution dimension alongside Owner (the tenant
// boundary): the project WITHIN the org that owns this machine. Not part of the
// (Owner, Name) primary key — an additive, Sync2-safe column that defaults to
// "" (the org's default project), so existing rows and callers are unaffected.
Project string `xorm:"varchar(100)" json:"project"`
Id string `xorm:"varchar(100)" json:"id"`
Provider string `xorm:"varchar(100)" json:"provider"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
ExpireTime string `xorm:"varchar(100)" json:"expireTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Region string `xorm:"varchar(100)" json:"region"`
Zone string `xorm:"varchar(100)" json:"zone"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
Size string `xorm:"varchar(100)" json:"size"`
Tag string `xorm:"varchar(500)" json:"tag"`
State string `xorm:"varchar(100)" json:"state"`
Image string `xorm:"varchar(100)" json:"image"`
Os string `xorm:"varchar(100)" json:"os"`
PublicIp string `xorm:"varchar(100)" json:"publicIp"`
PrivateIp string `xorm:"varchar(100)" json:"privateIp"`
CpuSize string `xorm:"varchar(100)" json:"cpuSize"`
MemSize string `xorm:"varchar(100)" json:"memSize"`
// DB info
RemoteProtocol string `xorm:"varchar(100)" json:"remoteProtocol"`
RemotePort int `json:"remotePort"`
RemoteUsername string `xorm:"varchar(100)" json:"remoteUsername"`
RemotePassword string `xorm:"varchar(100)" json:"remotePassword"`
}
func CreateMachineCloud ¶
func CreateMachineCloud(owner string, providerName string, spec *service.CreateMachineSpec) (*Machine, error)
CreateMachineCloud launches a new VM via the cloud provider and registers it in the DB.
func GetMachine ¶
func GetMachines ¶
func GetMaskedMachines ¶
func GetPaginationMachines ¶
type MeterLease ¶
type MeterLease struct {
Hour string `xorm:"varchar(12) notnull pk" json:"hour"` // UTC "YYYYMMDDHH" bucket — the billable unit.
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
}
MeterLease is a single-flight lease for the hourly compute-metering sweep. Its whole purpose is a MONEY-SAFETY invariant: the visor Deployment runs replicas: 2 with no leader election, so without a lease BOTH replicas would run service.MeterRunningMachines every hour and debit every running machine twice (the per-machine hour-bucketed RequestID is only a dedup HINT — commerce's RecordUsage does NOT dedup the withdraw transaction on requestId, so the client key alone does not stop a duplicate debit). The lease makes exactly one replica perform the sweep per wall-clock hour, cluster-wide.
The mechanism is the same insert-once-wins primitive the rest of object uses: Hour is the PK, so only the FIRST Insert for a given hour succeeds; a concurrent replica's Insert fails the unique-key constraint and that replica skips the hour. It survives a mid-hour restart too — a restarted replica finds the hour already claimed and does not re-sweep (so a rollout cannot re-bill the hour).
type MigrationReport ¶
type MigrationReport struct {
Table string
SourceRows int
WrittenRows int
Orgs int // distinct owner DBs the rows fanned into
}
MigrationReport records what MigratePostgresToBase copied for one table.
func MigratePostgresToBase ¶
func MigratePostgresToBase(src *relational.Engine, dst *baseStore) ([]MigrationReport, error)
MigratePostgresToBase copies the per-tenant visor tables from the shared Postgres engine into per-org Base SQLite files, routing each row to DBPath(owner). It iterates perOrgModels() -- the SAME registry the Base schema sync uses -- so it can never drift from what an org DB actually holds. The shared tables (Plan catalog, MeterLease lease) are deliberately NOT migrated: they stay on the shared Postgres coordination engine under Base mode (see LLM.md, Base backend: shared vs per-org).
It NEVER runs at boot: an operator invokes it explicitly during the cutover window (e.g. a `visor migrate` subcommand). Rows are grouped by their Owner field; the rare row with an empty Owner (e.g. a Record whose Organization was unset) routes to the _global sentinel DB. This mirrors hanzo/cloud's introspective migration/pg_to_sqlite.go, but is schema-aware because visor owns its models rather than a drifted upstream schema.
type NodePool ¶
type NodePool struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
ClusterID string `xorm:"varchar(100)" json:"clusterId"`
PoolID string `xorm:"varchar(100)" json:"poolId"`
Provider string `xorm:"varchar(100)" json:"provider"`
Size string `xorm:"varchar(100)" json:"size"`
Count int `json:"count"`
MinNodes int `json:"minNodes"`
MaxNodes int `json:"maxNodes"`
AutoScale bool `json:"autoScale"`
State string `xorm:"varchar(100)" json:"state"`
CostPerHour int64 `json:"costPerHour"` // cents
OrgID string `xorm:"varchar(100)" json:"orgId"`
ProjectID string `xorm:"varchar(100)" json:"projectId"`
}
func CreateNodePoolCloud ¶
func CreateNodePoolCloud(owner, providerName, clusterID string, spec *service.CreateNodePoolSpec) (*NodePool, error)
CreateNodePoolCloud creates a new node pool in DOKS via the cloud provider and persists it.
func GetNodePool ¶
func GetNodePools ¶
func GetPaginationNodePools ¶
type Patch ¶
type Patch struct {
Name string `json:"name"`
Category string `json:"category"`
Title string `json:"title"`
Url string `json:"url"`
Size string `json:"size"`
ExpectedStatus string `json:"expectedStatus"`
Status string `json:"status"`
InstallTime string `json:"installTime"`
Message string `json:"message"`
}
type Plan ¶
type Plan struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Description string `xorm:"varchar(500)" json:"description"`
Category string `xorm:"varchar(100)" json:"category"` // "bot", "standard", "pro", "power"
State string `xorm:"varchar(100)" json:"state"` // "Active", "Inactive"
// Specs advertised to the customer
VCpu int `json:"vCpu"`
Ram int `json:"ram"` // MB
Disk int `json:"disk"` // GB
CpuType string `xorm:"varchar(50)" json:"cpuType"` // "shared", "dedicated"
// Pricing (cents/mo)
PriceMonthly int `json:"priceMonthly"` // customer price in cents USD
// Region availability
Regions string `xorm:"varchar(500)" json:"regions"` // comma-separated: "us,eu,sg"
// Provider mapping per region (JSON)
// e.g. {"us":{"provider":"Hetzner","serverType":"cpx31","location":"ash"},...}
ProviderMapping string `xorm:"mediumtext" json:"providerMapping"`
// Traffic included (GB)
TrafficIncluded int `json:"trafficIncluded"`
SortOrder int `json:"sortOrder"`
}
Plan represents a resale VM plan available to customers.
func DefaultPlans ¶
DefaultPlans returns the Hanzo Cloud plan catalog. Provider mapping is internal — customers never see backend provider names.
Regions: "us" (ash/hil), "eu" (fsn1/nbg1), "sg" (sin) Pricing aligned with hanzo/pricing cloudPlans.
func GetAllPlans ¶
func GetPlans ¶
Plan is a global read-only catalog: identical for every org, so it lives on the shared engine (Shared(), Postgres under both backends), never duplicated into per-org SQLite. The owner column is retained so white-label brands can scope their own catalog, but the physical table is shared. All Plan CRUD therefore routes through Shared().
type Provider ¶
type Provider struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
// Project is the attribution dimension alongside Owner: the project WITHIN the
// org that owns this BYOC provider. Additive, Sync2-safe, defaults to "".
Project string `xorm:"varchar(100)" json:"project"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
Region string `xorm:"varchar(100)" json:"region"`
Network string `xorm:"varchar(100)" json:"network"`
Chain string `xorm:"varchar(100)" json:"chain"`
BrowserUrl string `xorm:"varchar(200)" json:"browserUrl"`
State string `xorm:"varchar(100)" json:"state"`
ProviderUrl string `xorm:"varchar(200)" json:"providerUrl"`
ClusterID string `xorm:"varchar(100)" json:"clusterId"` // DOKS cluster UUID
// CostReadScope carries the per-cloud identifier the fleet-billing cost collector
// needs to read this BYOC account's spend, beyond the (ClientId, ClientSecret,
// Region) triple used to manage machines. It is cloud-specific and additive
// (Sync2-safe, defaults ""):
// AWS — ignored (Cost Explorer is account-wide from the access key).
// DO — ignored (the balance endpoint is account-wide from the token).
// Azure— "<tenantId>/<subscriptionId>" (Cost Management query scope + auth tenant).
// GCP — "<project>.<dataset>.<table>" of the BigQuery billing-export table.
// Empty means "cost-read not configured": the collector honestly skips this
// provider (no fee) rather than fabricating spend.
CostReadScope string `xorm:"varchar(300)" json:"costReadScope"`
}
func GetAllActiveCloudProviders ¶
GetAllActiveCloudProviders returns every org's active BYOC cloud provider — the set the daily fleet cost collector bills 1% of spend against. Like the running- machine sweep it scans cross-tenant and each row carries its own Owner/Project, so one pass attributes every org's cloud fee correctly.
func GetMaskedProvider ¶
func GetMaskedProviders ¶
func GetPaginationProviders ¶
func GetProvider ¶
func GetProviders ¶
type Record ¶
type Record struct {
Id int `xorm:"int notnull pk autoincr" json:"id"`
Owner string `xorm:"varchar(100) index" json:"owner"`
Name string `xorm:"varchar(100) index" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Organization string `xorm:"varchar(100)" json:"organization"`
ClientIp string `xorm:"varchar(100)" json:"clientIp"`
UserAgent string `xorm:"varchar(100)" json:"userAgent"`
User string `xorm:"varchar(100)" json:"user"`
Method string `xorm:"varchar(100)" json:"method"`
RequestUri string `xorm:"varchar(1000)" json:"requestUri"`
Action string `xorm:"varchar(1000)" json:"action"`
Language string `xorm:"varchar(100)" json:"language"`
Object string `xorm:"mediumtext" json:"object"`
Response string `xorm:"mediumtext" json:"response"`
Provider string `xorm:"varchar(100)" json:"provider"`
Block string `xorm:"varchar(100)" json:"block"`
Transaction string `xorm:"varchar(500)" json:"transaction"`
IsTriggered bool `json:"isTriggered"`
}
func GetPaginationRecords ¶
func GetRecords ¶
type Session ¶
type Session struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
StartTime string `xorm:"varchar(100)" json:"startTime"`
EndTime string `xorm:"varchar(100)" json:"endTime"`
Protocol string `xorm:"varchar(20)" json:"protocol"`
ConnectionId string `xorm:"varchar(50)" json:"connectionId"`
Asset string `xorm:"varchar(200) index" json:"asset"`
Creator string `xorm:"varchar(36) index" json:"creator"`
ClientIp string `xorm:"varchar(200)" json:"clientIp"`
UserAgent string `xorm:"varchar(200)" json:"userAgent"`
ClientIpDesc string `xorm:"varchar(100)" json:"clientIpDesc"`
UserAgentDesc string `xorm:"varchar(100)" json:"userAgentDesc"`
Width int `json:"width"`
Height int `json:"height"`
Status string `xorm:"varchar(20) index" json:"status"`
Recording string `xorm:"varchar(1000)" json:"recording"`
Code int `json:"code"`
Message string `json:"message"`
Mode string `xorm:"varchar(10)" json:"mode"`
Operations []string `xorm:"json varchar(1000)" json:"operations"`
Reviewed bool `json:"reviewed"`
CommandCount int64 `json:"commandCount"`
}
func CreateSession ¶
func GetConnSession ¶
func GetPaginationSessions ¶
func GetSessions ¶
func GetSessionsByStatus ¶
GetSessionsByStatus lists sessions in any of the given statuses across ALL orgs -- a cluster-wide sweep the stale-session GC ticker runs. Under Postgres this is one query over the single engine; under Base it unions the per-org SQLite DBs (allEngines), since there is no single table spanning tenants.
type StorageBackend ¶
type StorageBackend string
StorageBackend names the configured persistence backend.
const ( // BackendPostgres is the historical shared-Postgres backend (default). BackendPostgres StorageBackend = "postgres" // BackendBase is the per-org SQLite substrate (hanzoai/base, HIP-0302). BackendBase StorageBackend = "base" )
func ConfiguredBackend ¶
func ConfiguredBackend() StorageBackend
ConfiguredBackend reads the storageBackend config knob. Default is Base (SQLite for everything — the house rule); a multi-instance deployment opts INTO Postgres with storageBackend=postgres.
type Volume ¶
type Volume struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
// Project is the attribution dimension alongside Owner: the project WITHIN the
// org that owns this volume. Additive, Sync2-safe, defaults to "".
Project string `xorm:"varchar(100)" json:"project"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Id string `xorm:"varchar(100)" json:"id"` // Cloud provider volume ID
Provider string `xorm:"varchar(100)" json:"provider"` // Provider name
Machine string `xorm:"varchar(100)" json:"machine"` // Attached machine name (empty if detached)
Region string `xorm:"varchar(100)" json:"region"`
Size int `json:"size"` // GB
State string `xorm:"varchar(100)" json:"state"` // "Available", "Attached", "Creating"
Format string `xorm:"varchar(50)" json:"format"` // "ext4", "xfs", etc.
MountPoint string `xorm:"varchar(200)" json:"mountPoint"` // e.g. "/mnt/data"
}
Volume represents a block storage volume attached to a machine.
func CreateVolumeCloud ¶
func GetVolumes ¶
type WhitelabelConfig ¶
type WhitelabelConfig struct {
AppName string `json:"appName"`
LogoUrl string `json:"logoUrl"`
FaviconUrl string `json:"faviconUrl"`
PrimaryColor string `json:"primaryColor"`
SupportUrl string `json:"supportUrl"`
DocsUrl string `json:"docsUrl"`
OrgFilter string `json:"orgFilter"`
}
WhitelabelConfig holds per-hostname branding configuration.
func GetWhitelabelConfig ¶
func GetWhitelabelConfig(host string) *WhitelabelConfig
GetWhitelabelConfig returns the branding config for a given hostname. Falls back to the default (Hanzo) config if no match is found.
Source Files
¶
- adapter.go
- agent_binding.go
- asset.go
- auth.go
- billing_lease.go
- coordinator.go
- cost_cursor.go
- fleet_worker.go
- machine.go
- machine_cloud.go
- meter_lease.go
- migrate_base.go
- node_pool.go
- plan.go
- plan_seed.go
- provider.go
- provider_util.go
- record.go
- record_chain.go
- session.go
- store.go
- store_base.go
- store_pg.go
- store_replica.go
- tenant_context.go
- volume.go
- volume_cloud.go
- whitelabel.go