Files
compute/k8s/configmap.yaml
zeekayandHanzo Dev 86f85934e1 visor is the multi-cloud path: no Kubernetes client at all
visor manages VMs across DO/Hetzner/Lightsail/Azure/GCP/Proxmox. It is
deliberately the non-cluster path, and it linked client-go twice.

autoscaler/ is deleted. It listed Pending pods via rest.InClusterConfig and
scaled DOKS node pools — a cluster autoscaler living inside the VM manager. It
was gated on autoscalerClusters, which is set in no deployment, so it had never
run. service/doks.go is untouched: pure godo, still serving node-pool CRUD.

object/coordinator.go now elects over ha.Membership — the interface hanzoai/ha
already defines, reused rather than re-declared. With nothing registered the
default reports an ERROR, not an empty set: an empty set is the positive claim
"I have no peers", which is exactly the lie that double-debits a two-replica
Deployment. Only an error carries "I don't know who my peers are".

That default means no source, no metering, so a source had to be chosen. It is
ha.Static in main.go's serve(), paired with replicas: 1 in universe. The sole
process is the sole writer, so exactly-once holds by construction. It is
registered in serve() and NOT in visor.Bootstrap(), which is shared verbatim
with the embedded cloud mount whose replica count is cloud's, not ours.

The two halves are one change: ha.Static at replicas: 2 bills every customer
twice an hour. Both sides now carry a comment pointing at the other.

Also corrects the comment that made this invisible. Shared() said "Postgres
under both backends"; under Base it is the pod-local _global SQLite coord, so
the lease PK does not span pods and the election is the only thing holding the
line — not belt-and-braces over a global constraint, as the doc implied.

k8s/rbac.yaml is a bare ServiceAccount: visor holds zero cluster permissions.
go.mod loses every k8s.io/* and sigs.k8s.io/* entry, not just client-go.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 19:07:11 -07:00

73 lines
1.9 KiB
YAML

apiVersion: v1
kind: Secret
metadata:
name: visor-config
namespace: hanzo
type: Opaque
stringData:
app.conf: |
appname = hanzo-visor
httpport = 19000
runmode = prod
SessionOn = true
copyrequestbody = true
driverName = postgres
dataSourceName = ${DB_DSN}
dbName = hanzo_visor
tableNamePrefix =
isDemoMode = false
logPostOnly = true
guacamoleEndpoint = 127.0.0.1:4822
iamEndpoint = http://iam.hanzo.svc.cluster.local:8000
clientId = ${IAM_CLIENT_ID}
clientSecret = ${IAM_CLIENT_SECRET}
iamOrganization = "hanzo"
iamApplication = "app-hanzo-visor"
redirectPath = /callback
isLocalIpDb = false
publicDomain = ""
adminDomain = ""
enableExtraPages = false
defaultLanguage = "en"
staticBaseUrl = "https://cdn.hanzo.ai"
htmlTitle = "Hanzo Visor"
faviconUrl = "https://cdn.hanzo.ai/static/favicon.png"
logoUrl = "https://cdn.hanzo.ai/img/hanzo-visor-logo.png"
showGithubCorner = false
defaultColorPrimary = "#fd4444"
logConfig = {"adapter":"console"}
commerceUrl = ${COMMERCE_URL}
commerceToken = ${COMMERCE_TOKEN}
---
# Credentials should be provisioned via Hanzo KMS or external-secrets operator.
# Example ExternalSecret (if using external-secrets):
#
# apiVersion: external-secrets.io/v1beta1
# kind: ExternalSecret
# metadata:
# name: visor-credentials
# namespace: hanzo
# spec:
# refreshInterval: 1h
# secretStoreRef:
# name: hanzo-kms
# kind: ClusterSecretStore
# target:
# name: visor-env
# data:
# - secretKey: DB_DSN
# remoteRef:
# key: visor/db-dsn
# - secretKey: IAM_CLIENT_ID
# remoteRef:
# key: visor/iam-client-id
# - secretKey: IAM_CLIENT_SECRET
# remoteRef:
# key: visor/iam-client-secret
# - secretKey: COMMERCE_URL
# remoteRef:
# key: visor/commerce-url
# - secretKey: COMMERCE_TOKEN
# remoteRef:
# key: visor/commerce-token