Documentation
¶
Index ¶
- Constants
- Variables
- func ComposeImageRef(image, tag string) string
- func EngineSupportsLogicalDatabases(e DBEngine) bool
- func EngineUsesLogicalDatabaseRecord(e DBEngine) bool
- func IsNotifiable(t AppEventType) bool
- func IsReservedMetadataKey(key string) bool
- func IsValidPermission(p Permission) bool
- func IsValidResourceType(t string) bool
- func IsValidSIEMSink(s string) bool
- func NormalizeSecurityProfile(p string) string
- func RoleHasPermission(role WorkspaceRole, p Permission) bool
- func RolePermissions(role WorkspaceRole) map[Permission]bool
- func SplitImageRef(ref string) (image, tag string)
- func ValidAccessMode(m ServerAccessMode) bool
- func ValidAppBuildMethod(m AppBuildMethod) bool
- func ValidDNSProviderType(t string) bool
- func ValidDeployStrategy(s DeployStrategy) bool
- func ValidHealthcheckType(t HealthcheckType) bool
- func ValidImagePullPolicy(p ImagePullPolicy) bool
- func ValidRestartPolicy(p RestartPolicy) bool
- func ValidRunnerScope(s RunnerScope) bool
- func ValidRuntimeKind(k RuntimeKind) bool
- func ValidVolumeAccessMode(m VolumeAccessMode) bool
- type ACMEAccount
- type APIKey
- type Alert
- type AlertCategory
- type AlertSeverity
- type AlertState
- type AnalyticsRollup
- type AppBuildMethod
- type AppEnvVar
- type AppEvent
- type AppEventSeverity
- type AppEventType
- type AppMount
- type AppPort
- type AppSourceType
- type AppStatus
- type Application
- type AuditLog
- type Backup
- type BackupSchedule
- type BackupStatus
- type Certificate
- type CronJob
- type CustomRole
- type DBEngine
- type DBStatus
- type DNSProvider
- type DNSRecord
- type Database
- type DatabaseInstance
- type DeployStrategy
- type Deployment
- type DeploymentStatus
- type Domain
- type DomainStatus
- type DomainTLSMode
- type Environment
- type GPUDevice
- type GPUVendor
- type GatewayUpdateProgress
- type GitAuthType
- type GitRepository
- type GitSource
- type GitSourceStatus
- type GitSyncPolicy
- type HealthcheckType
- type Image
- type ImagePullPolicy
- type InvitationStatus
- type Job
- type JobStatus
- type LDAPConfig
- type LDAPGroupMapping
- type LeaseKind
- type License
- type Metadata
- func DefaultManagedBy(m Metadata, value string) Metadata
- func DefaultOwner(m Metadata, kind string, id uint, name string) Metadata
- func MergeUserMetadata(current, overlay Metadata) Metadata
- func SanitizeUserMetadata(in Metadata) Metadata
- func SetBuiltin(m Metadata, kv ...string) Metadata
- func SetOwner(m Metadata, kind string, id uint, name string) Metadata
- type MetricSample
- type Middleware
- type Network
- type NetworkAllocation
- type NodePlacement
- type Notification
- type NotificationChannel
- type NotificationChannelType
- type OAuthProvider
- type OAuthProviderType
- type Organization
- type OwnerRef
- type PasswordResetToken
- type Permission
- type PipelineDefinition
- type PipelineRun
- type PipelineRunStatus
- type PipelineRunSummary
- type PipelineStepRun
- type Plan
- type PlatformBackup
- type PlatformBackupSettings
- type PlatformBackupSubject
- type PortBinding
- type PortBindingStatus
- type Registry
- type RegistrySettings
- type Release
- type ReleaseApproval
- type ResourcePolicy
- type RestartPolicy
- type Route
- type RouteStatus
- type RouteTLSMode
- type Runner
- type RunnerLease
- type RunnerLeaseStatus
- type RunnerScope
- type RunnerStatus
- type RuntimeKind
- type SAMLConfig
- type SCIMToken
- type SIEMConfig
- type Secret
- type Server
- type ServerAccessMode
- type ServerConnectivity
- type ServerRole
- type ServerStatus
- type ServiceUpdateConfig
- type Session
- type Setting
- type SettingType
- type Stack
- type StackEnvVar
- type SystemRole
- type Template
- type TemplateInstall
- type TemplateSource
- type TemplateSourceType
- type TwoFactorRecoveryCode
- type UIDModel
- type UpdateStatus
- type UpgradeProgress
- type UpgradeStep
- type User
- type Volume
- type VolumeAccessMode
- type VolumeBackup
- type Webhook
- type WebhookDelivery
- type WebhookDeliveryStatus
- type Workspace
- type WorkspaceBackupSettings
- type WorkspaceInvitation
- type WorkspaceKey
- type WorkspaceMember
- type WorkspaceQuota
- type WorkspaceRole
- type WorkspaceWithRole
Constants ¶
const ( ScopeRead = "read" // read-only access to resources ScopeWrite = "write" // create/update/delete resources ScopeDeploy = "deploy" // trigger deployments and lifecycle actions ScopeAdmin = "admin" // administrative operations ScopeAll = "*" // all scopes // Registry-only scopes: a key carrying ONLY these may pull/push images but is // rejected by the rest of the API (least-privilege CI/registry credentials). ScopeRegistryRead = "registry_read" // pull from the container registry ScopeRegistryWrite = "registry_write" // push to the container registry )
API key scopes gate what an API-key caller may do. An empty scope set means read-only (least privilege); "*" grants everything.
const ( // CertSourceImported is a bring-your-own certificate the user uploaded. CertSourceImported = "imported" // CertSourceACME is a certificate Miabi issued via ACME DNS-01 using a // connected DNS provider (managed; auto-renewable). CertSourceACME = "acme" )
Certificate sources.
const ( CertStatusActive = "active" // issued and in use CertStatusIssuing = "issuing" // issuance in progress CertStatusFailed = "failed" // last issuance/renewal failed (see LastError) )
Managed-certificate (ACME) statuses.
const ( DNSProviderCloudflare = "cloudflare" DNSProviderRoute53 = "route53" DNSProviderDigitalOcean = "digitalocean" )
DNS provider types Miabi can connect to. Each maps to a libdns module; the credential shape differs per type (see the dns package). "manual" is the implicit default for a domain with no connected provider (copy-paste flow).
const ( DNSProviderStatusOK = "ok" DNSProviderStatusError = "error" )
DNSProviderStatus is the last-known health of a connection.
const ( DNSRecordPurposeVerification = "verification" // _miabi-challenge ownership TXT DNSRecordPurposeAppAddress = "app-address" // app A/AAAA/CNAME )
DNSRecord purposes — why Miabi created a managed record.
const ( JobSourceManual = "manual" JobSourceAPI = "api" JobSourceScheduled = "scheduled" )
Job sources.
const ( ConcurrencyAllow = "allow" // always spawn a new Job ConcurrencyForbid = "forbid" // skip the tick if a run is still active ConcurrencyReplace = "replace" // cancel the in-flight run, then spawn )
CronJob concurrency policies (Kubernetes parity): what to do when a tick fires while the previous run is still active.
const ( LDAPTLSNone = "none" // plain LDAP (discouraged; dev/LAN only) LDAPTLSStartTLS = "starttls" // upgrade a plain connection with StartTLS LDAPTLSLDAPS = "ldaps" // implicit TLS (usually port 636) )
LDAP TLS modes for the directory connection.
const ( MetaManagedBy = "miabi.io/managed-by" // origin: see ManagedBy* values MetaTemplate = "miabi.io/template" // marketplace template slug MetaTemplateVersion = "miabi.io/template-version" // marketplace template version MetaTemplateInstall = "miabi.io/template-install" // template install id MetaStack = "miabi.io/stack" // owning stack docker name MetaGitOpsSource = "miabi.io/gitops-source" // id of the GitOps project that created the resource // MetaRuntimeAutoService marks an app whose service runtime was auto-defaulted // by cluster mode (not chosen by the caller). It is a one-shot marker: the first // deploy re-evaluates the choice — downgrading to a container if the app turns // out to hold node-local state — then clears it, so a later explicit runtime // choice is always respected. Value: "true". MetaRuntimeAutoService = "miabi.io/runtime-auto-service" // MetaDeclarativeName records the declarative/manifest resource name a logical // database was provisioned for. A manifest Database may live as a dedicated // instance or as a logical database sharing another instance, so this tag is // how a reconcile maps the manifest name back to the exact logical database it // owns (instead of guessing by instance name / first-database). MetaDeclarativeName = "miabi.io/declarative-name" // Owner reference: what entity this resource belongs to / whose lifecycle it // follows. Distinct from managed-by (the *mechanism* of creation); owner is // the *parent*, stored as kind+id+name so the UI can link to it without a // join. See OwnerKind* values and SetOwner/Owner. MetaOwnerKind = "miabi.io/owner-kind" // one of OwnerKind* values MetaOwnerID = "miabi.io/owner-id" // numeric id of the owner (omitted when 0) MetaOwnerName = "miabi.io/owner-name" // owner display name (for rendering + linking) )
Built-in metadata keys (all under MetadataReservedPrefix).
const ( ManagedByUser = "user" // created by hand ManagedByMarketplace = "marketplace" // installed from a marketplace template ManagedByStack = "stack" // created as part of a stack ManagedByStackImport = "stack-import" // created by importing a compose file )
ManagedBy values record how a resource came to exist.
const ( OwnerUser = "user" // a person created it for their own use (owner id = user id) OwnerApp = "app" // it backs an application OwnerDatabase = "database" // it backs a database instance OwnerStack = "stack" // it belongs to a stack )
OwnerKind values classify what a resource belongs to (the MetaOwnerKind value).
const ( NetAllocKindWorkspace = "workspace" // a workspace's default/user network NetAllocKindStack = "stack" // a per-stack network NetAllocKindGateway = "gateway" // the shared reverse-proxy network NetAllocKindOverlay = "overlay" // a per-workspace swarm overlay // NetAllocKindReserved marks a subnet that is not Miabi's but overlaps the pool // (a pre-existing/unmanaged Docker network), so the allocator never hands it out. NetAllocKindReserved = "reserved" )
Network-allocation kinds, recorded for observability and to distinguish real managed networks from subnets merely reserved to avoid an overlap.
const ( ConfigBotToken = "bot_token" // telegram; encrypted at rest ConfigChatID = "chat_id" // telegram ConfigWebhookURL = "webhook_url" // slack/discord; encrypted at rest )
Config keys used by the channel types.
const ( // SecurityProfileDefault runs containers as the image's default user with no // extra hardening — the platform's historical behavior. SecurityProfileDefault = "default" // SecurityProfileRestricted forces app/job containers to run as the platform // non-root UID (UID:0) with no-new-privileges and NET_RAW dropped — like // OpenShift's restricted SCC. May break images that require root. SecurityProfileRestricted = "restricted" )
SecurityProfile values harden how a workspace's application and job containers run. The zero value ("") is treated as SecurityProfileDefault, which keeps the Plan model's GORM zero-value-omission invariant intact (see the limits block).
const ( RegistryStorageFilesystem = "filesystem" RegistryStorageS3 = "s3" )
Registry storage driver values for RegistrySettings.StorageType.
const ( ResourceTypeApp = "app" ResourceTypeDatabase = "database" ResourceTypeDomain = "domain" )
Resource types a per-resource policy may target.
const ( SIEMSinkSyslog = "syslog" SIEMSinkWebhook = "webhook" SIEMSinkS3 = "s3" )
SIEM sink kinds.
const ( SIEMFormatJSON = "json" // NDJSON SIEMFormatCEF = "cef" // syslog CEF )
SIEM payload formats.
const ( VolumeDriverLocal = "local" VolumeDriverNFS = "nfs" VolumeDriverCIFS = "cifs" // VolumeDriverHost is a bind to an operator-managed host path (under /mnt/*), // not a Docker volume. It is meant for storage the operator has mounted at the // SAME path on every node (e.g. a NAS), so a replicated service can share it; // AccessMode is rwx. Privileged workspaces only. HostPath carries the path. VolumeDriverHost = "host" )
Volume driver names Miabi understands. The NFS/CIFS variants use Docker's built-in local driver with mount options (no external plugin required).
const DefaultOrganizationName = "default"
DefaultOrganizationName is the handle of the seeded default organization.
const DefaultRegistryVolume = "mb-registry-data"
DefaultRegistryVolume is the managed data volume for the filesystem driver.
const MetadataReservedPrefix = "miabi.io/"
MetadataReservedPrefix marks built-in, platform-managed metadata keys.
const Unlimited = -1
Unlimited is the limit value meaning "no cap". Distinct from 0 ("none allowed"), so a plan can forbid a resource entirely.
const UnlimitedPlanName = "Unlimited"
UnlimitedPlanName is the name of the built-in plan with no limits and all capabilities (seeded by SeedPlans). The platform system workspace is pinned to it so platform-managed infrastructure is never constrained by quotas.
Variables ¶
var NotifiableEvents = []AppEventType{ EventDeployStarted, EventDeploySucceeded, EventDeployFailed, EventContainerStarted, EventContainerStopped, EventContainerDied, EventContainerOOM, }
NotifiableEvents is the curated set of application lifecycle events that webhooks and notification channels may subscribe to. Configuration changes and other timeline noise are intentionally excluded.
var ValidScopes = map[string]bool{ ScopeRead: true, ScopeWrite: true, ScopeDeploy: true, ScopeAdmin: true, ScopeAll: true, ScopeRegistryRead: true, ScopeRegistryWrite: true, }
ValidScopes is the set of scopes accepted on API-key creation.
Functions ¶
func ComposeImageRef ¶
ComposeImageRef appends tag (defaulting to "latest") to a bare image, leaving refs that already specify a tag or digest untouched.
func EngineSupportsLogicalDatabases ¶
EngineSupportsLogicalDatabases reports whether an engine can host named per-app databases with their own users (the SQL engines, plus MongoDB which has databases with scoped users). Redis cannot.
func EngineUsesLogicalDatabaseRecord ¶
EngineUsesLogicalDatabaseRecord reports whether an engine hangs its backups and app connection injection off a logical Database row. This is the set of engines from EngineSupportsLogicalDatabases plus libSQL: libSQL hosts a single database (users cannot create more) but Miabi auto-creates one implicit Database row at provision time so the existing per-database backup and injection pipeline is reused unchanged. Redis, which has neither logical databases nor backups, is the only engine excluded.
func IsNotifiable ¶
func IsNotifiable(t AppEventType) bool
IsNotifiable reports whether an event type may trigger outbound notifications.
func IsReservedMetadataKey ¶
IsReservedMetadataKey reports whether key is platform-managed (built-in).
func IsValidPermission ¶
func IsValidPermission(p Permission) bool
IsValidPermission reports whether p is a known catalogued permission.
func IsValidResourceType ¶
IsValidResourceType reports whether t is a supported resource type.
func IsValidSIEMSink ¶
IsValidSIEMSink reports whether s is a supported sink kind.
func NormalizeSecurityProfile ¶
NormalizeSecurityProfile maps the empty/zero value to the default profile.
func RoleHasPermission ¶
func RoleHasPermission(role WorkspaceRole, p Permission) bool
RoleHasPermission reports whether a built-in role grants a permission.
func RolePermissions ¶
func RolePermissions(role WorkspaceRole) map[Permission]bool
RolePermissions returns the permission set of a built-in role as a copy.
func SplitImageRef ¶
SplitImageRef is the inverse of ComposeImageRef: it splits a pull reference into its repository and tag (e.g. "ghcr.io/org/app:v2" -> "ghcr.io/org/app", "v2"). A digest-pinned ref is returned as the image with an empty tag.
func ValidAccessMode ¶
func ValidAccessMode(m ServerAccessMode) bool
ValidAccessMode reports whether m is a known access mode.
func ValidAppBuildMethod ¶
func ValidAppBuildMethod(m AppBuildMethod) bool
ValidAppBuildMethod reports whether m is a known build method.
func ValidDNSProviderType ¶
ValidDNSProviderType reports whether t is a known provider type.
func ValidDeployStrategy ¶
func ValidDeployStrategy(s DeployStrategy) bool
ValidDeployStrategy reports whether s is a known strategy.
func ValidHealthcheckType ¶
func ValidHealthcheckType(t HealthcheckType) bool
ValidHealthcheckType reports whether t is a known healthcheck type.
func ValidImagePullPolicy ¶
func ValidImagePullPolicy(p ImagePullPolicy) bool
ValidImagePullPolicy reports whether p is a known image pull policy.
func ValidRestartPolicy ¶
func ValidRestartPolicy(p RestartPolicy) bool
ValidRestartPolicy reports whether p is a known restart policy.
func ValidRunnerScope ¶
func ValidRunnerScope(s RunnerScope) bool
ValidRunnerScope reports whether s is a known runner scope.
func ValidRuntimeKind ¶
func ValidRuntimeKind(k RuntimeKind) bool
ValidRuntimeKind reports whether k is a known runtime kind.
func ValidVolumeAccessMode ¶
func ValidVolumeAccessMode(m VolumeAccessMode) bool
ValidVolumeAccessMode reports whether m is a known access mode.
Types ¶
type ACMEAccount ¶
type ACMEAccount struct {
ID uint `json:"id" gorm:"primaryKey"`
CADirURL string `json:"ca_dir_url" gorm:"uniqueIndex;not null"`
Email string `json:"email" gorm:"not null"`
// KeyEnc is the PEM-encoded account private key, crypto.Encrypt'd at rest.
KeyEnc string `json:"-" gorm:"type:text;not null"`
// RegistrationJSON is the lego registration.Resource (account URL + body).
RegistrationJSON string `json:"-" gorm:"type:text"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
ACMEAccount is the platform's registered ACME account for a given CA directory, reused across all managed-certificate issuance. The account private key is encrypted at rest and never returned; the registration resource (account URL) is stored so issuance reuses the same account. Keyed by CADirURL so switching CA (e.g. staging -> production) registers a distinct account.
type APIKey ¶
type APIKey struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index;not null"`
WorkspaceID *uint `json:"workspace_id,omitempty" gorm:"index"`
Name string `json:"name" gorm:"not null"`
KeyHash string `json:"-" gorm:"uniqueIndex;not null"`
KeyPrefix string `json:"key_prefix" gorm:"index;not null"`
Scopes []string `json:"scopes" gorm:"serializer:json"`
AllowedIPs []string `json:"allowed_ips" gorm:"serializer:json"`
// ApplicationID narrows an ephemeral job key to a single application: it may
// act on (deploy) only that app, nothing else in the workspace. nil = the
// key is not app-bound (ordinary user keys).
ApplicationID *uint `json:"application_id,omitempty" gorm:"index"`
// Ephemeral marks a machine-minted, short-lived job/registry credential (issued
// per runner lease, revoked when the run goes terminal). Ephemeral keys are
// hidden from the API-keys UI, excluded from the MaxAPIKeys quota, and findable
// by the orphan sweeper.
Ephemeral bool `json:"ephemeral" gorm:"not null;default:false;index"`
LastUsedAt *time.Time `json:"last_used_at"`
ExpiresAt *time.Time `json:"expires_at"`
Revoked bool `json:"revoked" gorm:"default:false;not null"`
CreatedAt time.Time `json:"created_at"`
}
APIKey is a long-lived bearer credential for programmatic access, scoped to a user and (optionally) a workspace.
func (*APIKey) BoundToApp ¶
BoundToApp reports whether the key is restricted to a single application. A caller enforcing the deploy route uses this to reject a job key acting on any app other than the one it was minted for.
func (*APIKey) HasScope ¶
HasScope reports whether the key grants the given scope. An empty scope set defaults to read-only; "*" grants everything.
func (*APIKey) IsRegistryOnly ¶
IsRegistryOnly reports whether the key's scopes grant only registry access (so it must be refused by the general API). An empty scope set defaults to read, which is a general scope, so it is never registry-only.
type Alert ¶ added in v1.6.0
type Alert struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
RuleKey string `json:"rule_key" gorm:"not null"` // which built-in rule fired
Category AlertCategory `json:"category" gorm:"not null"`
Severity AlertSeverity `json:"severity" gorm:"not null"`
State AlertState `json:"state" gorm:"not null;default:firing"`
// DedupKey identifies the condition+subject. The partial unique index in the
// migration enforces at most one ACTIVE alert per (workspace, dedup_key).
DedupKey string `json:"dedup_key" gorm:"not null"`
SubjectType string `json:"subject_type"` // app | database | node | route | backup
SubjectRef string `json:"subject_ref"` // stable id/slug of the subject
SubjectLink string `json:"subject_link,omitempty"` // UI deep-link
// MinRole is the lowest workspace role that receives this alert (fan-out gate).
MinRole WorkspaceRole `json:"min_role" gorm:"not null;default:developer"`
Title string `json:"title"`
Body string `json:"body"`
Count int64 `json:"count" gorm:"not null;default:1"` // signals folded into this alert
Labels map[string]string `json:"labels,omitempty" gorm:"serializer:json"`
FirstSeen time.Time `json:"first_seen"`
LastSeen time.Time `json:"last_seen"`
ResolvedAt *time.Time `json:"resolved_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Alert is a workspace-level, stateful, deduplicated condition derived from signals (app events, metrics, jobs). It is shared across the workspace's members; per-user delivery is a Notification. The dedup key is the identity of the condition (e.g. "crashloop:app:42"): a repeat signal updates the existing active alert (bumping Count/LastSeen) instead of creating a new row, and a recovery signal resolves it — this is what turns 40 crash events into one line.
type AlertCategory ¶ added in v1.6.0
type AlertCategory string
AlertCategory groups alerts for role-based fan-out and UI filtering.
const ( CategoryDeploy AlertCategory = "deploy" CategoryRuntime AlertCategory = "runtime" CategoryDatabase AlertCategory = "database" CategoryStorage AlertCategory = "storage" CategoryTLS AlertCategory = "tls" CategoryNode AlertCategory = "node" CategoryCI AlertCategory = "ci" // runners / pipelines CategoryQuota AlertCategory = "quota" CategorySecurity AlertCategory = "security" )
type AlertSeverity ¶ added in v1.6.0
type AlertSeverity string
AlertSeverity ranks an alert's urgency. Only warning/critical notify by default; info lives in the inbox.
const ( AlertInfo AlertSeverity = "info" AlertWarning AlertSeverity = "warning" AlertCritical AlertSeverity = "critical" )
type AlertState ¶ added in v1.6.0
type AlertState string
AlertState is the alert's lifecycle position. An alert is a small FSM, not a log row: it fires, may be acknowledged, auto-resolves when the condition clears, and is archived after a retention window.
const ( AlertFiring AlertState = "firing" AlertAcknowledged AlertState = "acknowledged" AlertResolved AlertState = "resolved" AlertArchived AlertState = "archived" )
func (AlertState) Active ¶ added in v1.6.0
func (s AlertState) Active() bool
Active reports whether the alert is still an open condition (firing or acknowledged) — the states the dedup key is unique across.
type AnalyticsRollup ¶ added in v1.6.0
type AnalyticsRollup struct {
ID uint `gorm:"primaryKey" json:"-"`
WorkspaceID uint `gorm:"uniqueIndex:idx_arollup,priority:1;not null" json:"workspace_id"`
ApplicationID uint `gorm:"uniqueIndex:idx_arollup,priority:2;index" json:"application_id"`
RouteName string `gorm:"uniqueIndex:idx_arollup,priority:3;size:200" json:"route_name"`
Bucket time.Time `gorm:"uniqueIndex:idx_arollup,priority:4;index" json:"bucket"` // minute-truncated UTC
Requests int64 `json:"requests"`
BytesIn int64 `json:"bytes_in"`
BytesOut int64 `json:"bytes_out"`
Status2xx int64 `json:"status_2xx"`
Status3xx int64 `json:"status_3xx"`
Status4xx int64 `json:"status_4xx"`
Status5xx int64 `json:"status_5xx"`
// Latency histograms (per-bucket counts over analytics.LatencyBoundsMs, with a
// trailing overflow bucket) + sums for the mean.
DurationHist []int64 `gorm:"serializer:json" json:"-"`
UpstreamHist []int64 `gorm:"serializer:json" json:"-"`
DurationSum int64 `json:"-"` // milliseconds
UpstreamSum int64 `json:"-"`
// VisitorsHLL is the serialized HyperLogLog sketch of visitor ids for this
// bucket — mergeable, so a range's uniques is the merge of its buckets.
VisitorsHLL []byte `json:"-"`
// Bounded top-K count maps for the categorical breakdowns.
TopPaths map[string]int64 `gorm:"serializer:json" json:"-"`
TopReferrers map[string]int64 `gorm:"serializer:json" json:"-"`
TopCountries map[string]int64 `gorm:"serializer:json" json:"-"`
TopUAFamilies map[string]int64 `gorm:"serializer:json" json:"-"` // browser family
TopOS map[string]int64 `gorm:"serializer:json" json:"-"`
TopDevice map[string]int64 `gorm:"serializer:json" json:"-"` // desktop | mobile | tablet | bot
TopMethods map[string]int64 `gorm:"serializer:json" json:"-"`
// BotRequests is the subset of Requests classified as automated (crawlers,
// scripts) — the rest are treated as human, for the bot-vs-human split.
BotRequests int64 `json:"-"`
}
AnalyticsRollup is one minute-bucketed aggregate of a route's HTTP traffic, built by the analytics consumer from Goma's per-request event stream. Rows are keyed by (workspace, app, route, minute); queries merge the rows spanning a range. It powers the Traffic, Performance and Web Analytics dashboards without any per-request storage or app instrumentation.
Latency percentiles come from the histograms (DurationHist/UpstreamHist over analytics.LatencyBoundsMs); unique visitors from the mergeable HyperLogLog sketch (VisitorsHLL); the Top* maps are bounded top-K counts. None of it holds PII (the visitor id is a daily-salted hash produced at the edge; no IP).
type AppBuildMethod ¶
type AppBuildMethod string
AppBuildMethod selects how a git-source application's image is built. It only applies to git apps (image apps pull a prebuilt image).
const ( // BuildAuto inspects the cloned repository at build time: a Dockerfile in the // tree builds with Docker, otherwise Cloud Native Buildpacks build it. Default. BuildAuto AppBuildMethod = "auto" // BuildDockerfile always builds the repo's Dockerfile. BuildDockerfile AppBuildMethod = "dockerfile" // BuildBuildpack always builds with Cloud Native Buildpacks (no Dockerfile // required); the resulting image runs via the CNB launcher. BuildBuildpack AppBuildMethod = "buildpack" )
type AppEnvVar ¶
type AppEnvVar struct {
ID uint `json:"id" gorm:"primaryKey"`
ApplicationID uint `json:"application_id" gorm:"index:idx_env_app_key,unique;not null"`
Key string `json:"key" gorm:"index:idx_env_app_key,unique;not null"`
Value string `json:"value"` // plaintext for non-secret; ciphertext when IsSecret
IsSecret bool `json:"is_secret" gorm:"not null;default:false"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
AppEnvVar is an environment variable for an application. Secret values are encrypted at rest.
type AppEvent ¶
type AppEvent struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
ApplicationID uint `json:"application_id" gorm:"index:idx_event_app_id;not null"`
Type AppEventType `json:"type" gorm:"not null"`
Severity AppEventSeverity `json:"severity" gorm:"not null;default:info"`
Message string `json:"message"`
Metadata map[string]string `json:"metadata,omitempty" gorm:"serializer:json"`
ActorID *uint `json:"actor_id,omitempty"`
CreatedAt time.Time `json:"created_at" gorm:"index:idx_event_app_id"`
// Display fields are populated at delivery time (notifications, webhooks) and
// are never persisted. They let a renderer name the resource instead of
// showing a bare numeric id. Empty when the application has been deleted.
ApplicationName string `json:"application_name,omitempty" gorm:"-"`
ApplicationSlug string `json:"application_slug,omitempty" gorm:"-"`
}
AppEvent is a timeline entry for an application: lifecycle transitions (deploys, releases), runtime container events (start/stop/crash/health), and configuration changes. Distinct from AuditLog (user mutations, admin-scoped) and from deployment build logs.
type AppEventSeverity ¶
type AppEventSeverity string
AppEventSeverity colors an event in the UI.
const ( SeverityInfo AppEventSeverity = "info" SeverityWarning AppEventSeverity = "warning" SeverityError AppEventSeverity = "error" )
type AppEventType ¶
type AppEventType string
AppEventType is a coarse classification of an application event.
const ( EventDeployStarted AppEventType = "deploy.started" EventDeploySucceeded AppEventType = "deploy.succeeded" EventDeployFailed AppEventType = "deploy.failed" EventRollbackStarted AppEventType = "rollback.started" EventReleaseActivated AppEventType = "release.activated" EventContainerStarted AppEventType = "container.started" EventContainerStopped AppEventType = "container.stopped" EventContainerDied AppEventType = "container.died" EventContainerOOM AppEventType = "container.oom" EventContainerHealth AppEventType = "container.health" EventEnvUpdated AppEventType = "env.updated" EventDomainAttached AppEventType = "domain.attached" EventDomainVerified AppEventType = "domain.verified" EventVolumeAttached AppEventType = "volume.attached" EventVolumeDetached AppEventType = "volume.detached" EventSettingsUpdated AppEventType = "settings.updated" EventAppCreated AppEventType = "app.created" EventAppDeleted AppEventType = "app.deleted" )
type AppMount ¶
type AppMount struct {
VolumeID uint `json:"volume_id"`
DockerName string `json:"docker_name"`
Path string `json:"path"`
HostPreset string `json:"host_preset,omitempty"` // set => privileged host bind, not a volume
// HostPath is the bind source for a "host" driver volume (denormalized from the
// volume at attach time so the runtime binds it without a volume lookup). When
// set, the mount is a bind of HostPath, not a Docker named volume.
HostPath string `json:"host_path,omitempty"`
ReadOnly bool `json:"read_only,omitempty"`
}
AppMount attaches storage to an application at a container path. It is either a managed workspace volume (VolumeID/DockerName) or — for privileged workspaces only — an allow-listed host bind identified by HostPreset (see package hostmount). The two are mutually exclusive; the host source path is resolved server-side from the preset, never stored from client input.
type AppPort ¶
type AppPort struct {
ID uint `json:"id" gorm:"primaryKey"`
ApplicationID uint `json:"application_id" gorm:"index:idx_appport_unique,unique;not null"`
ContainerPort int `json:"container_port" gorm:"index:idx_appport_unique,unique;not null"`
Protocol string `json:"protocol" gorm:"index:idx_appport_unique,unique;not null;default:tcp"` // tcp | udp
// Scheme is the application protocol the container speaks on this port
// (http | https), used to build the Gateway backend URL. Transport stays in
// Protocol; this is L7. Defaults to http.
Scheme string `json:"scheme" gorm:"not null;default:http"`
Name string `json:"name"`
}
AppPort is a container port an application exposes. Developer-managed; no approval is required to declare one.
type AppSourceType ¶
type AppSourceType string
AppSourceType is how an application's image is obtained.
const ( AppSourceImage AppSourceType = "image" // pull a prebuilt image AppSourceGit AppSourceType = "git" // build from a Git repo )
type Application ¶
type Application struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_app_workspace_name,unique;not null"`
// Name is the unique, URL/CLI/docker handle (lowercase [a-z0-9-]) scoped to
// the workspace. Renamed from the former "slug"; the numeric ID/UID remain the
// stable internal references.
Name string `json:"name" gorm:"index:idx_app_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI. Renamed from the former
// "name"; not unique.
DisplayName string `json:"display_name"`
SourceType AppSourceType `json:"source_type" gorm:"not null;default:image"`
// Alias is the stable in-network DNS name + container hostname for the app,
// shaped "mb-app-<token>-<id>". Generated once at creation and reused as the
// reverse-proxy upstream so it survives redeploys. Empty on legacy apps,
// which fall back to "mb-app-<id>".
Alias string `json:"alias,omitempty" gorm:"index"`
// ServerID is the node this app runs on (0 = local control-plane node). In
// cluster mode (RuntimeService) it is a placement hint rather than a fixed
// node — Swarm schedules the tasks subject to PlacementConstraints.
ServerID uint `json:"server_id" gorm:"index;not null;default:0"`
// ServerName is the display name of the node (transient; populated on read so
// the UI can show where the app runs). Empty if the node is unknown.
ServerName string `json:"server_name,omitempty" gorm:"-"`
// Nodes is the real per-node replica placement of a cluster (RuntimeService)
// app: where the Swarm scheduler actually placed its running tasks (transient;
// populated only on the app-detail read, and only in cluster mode). Empty for
// container apps, whose single node is ServerName.
Nodes []NodePlacement `json:"nodes,omitempty" gorm:"-"`
// Cluster runtime. RuntimeKind defaults to "container" (single container on
// plain Docker); "service" runs the app as a replicated Swarm service on the
// workspace overlay (only when cluster mode is enabled). Replicas,
// PlacementConstraints and UpdateConfig apply to the service runtime.
RuntimeKind RuntimeKind `json:"runtime_kind" gorm:"not null;default:container"`
Replicas int `json:"replicas" gorm:"not null;default:1"`
PlacementConstraints []string `json:"placement_constraints,omitempty" gorm:"serializer:json"`
UpdateConfig *ServiceUpdateConfig `json:"update_config,omitempty" gorm:"serializer:json"`
// Icon is an optional logo for the app (a URL or an "mdi-…" class) shown in
// the UI. Set from a marketplace template on install; empty for ad-hoc apps,
// which fall back to a generated avatar.
Icon string `json:"icon,omitempty"`
// Image source. Image is the repository (e.g. "nginx", "ghcr.io/org/app");
// Tag is optional and defaults to "latest" when composing the pull ref.
Image string `json:"image"`
Tag string `json:"tag,omitempty"`
// Git source.
GitRepo string `json:"git_repo,omitempty"`
GitRef string `json:"git_ref,omitempty"`
// Build config (git source only). BuildMethod selects how the image is built:
// auto (Dockerfile if present, else buildpacks), dockerfile, or buildpack.
// Builder optionally overrides the buildpack builder image (empty = platform
// default); Buildpacks pins extra buildpacks to apply; BuildEnv supplies build
// arguments to the buildpack build. All are ignored for image-source apps.
BuildMethod AppBuildMethod `json:"build_method,omitempty" gorm:"not null;default:auto"`
Builder string `json:"builder,omitempty"`
Buildpacks []string `json:"buildpacks,omitempty" gorm:"serializer:json"`
BuildEnv map[string]string `json:"build_env,omitempty" gorm:"serializer:json"`
// Credentials. RegistryID selects a stored registry credential used to pull
// private images; GitRepositoryID selects a stored Git credential used to
// clone private repos. Both nil = anonymous (public) source.
RegistryID *uint `json:"registry_id,omitempty" gorm:"index"`
GitRepositoryID *uint `json:"git_repository_id,omitempty" gorm:"index"`
// StackID optionally groups the app under a Stack (nil = ungrouped). When
// set, the stack's DockerName is applied to the app's containers as the
// Docker Compose project label.
StackID *uint `json:"stack_id,omitempty" gorm:"index"`
// TemplateInstallID links the app back to the marketplace template install it
// was created from (nil = not installed from a template). Lets the UI show
// provenance ("installed from <template>") and jump to the install.
TemplateInstallID *uint `json:"template_install_id,omitempty" gorm:"index"`
// OfficialTemplate marks an app created from an *official* marketplace template.
// It is set by the platform at install time (never by user input) and is the
// trust boundary for the AllowOfficialImageUser plan capability, which lets such
// apps keep the image's default user under the restricted security profile.
OfficialTemplate bool `json:"official_template" gorm:"not null;default:false"`
// Metadata holds free-form labels (provenance, grouping, declarative/GitOps).
// Keys under the reserved "miabi.io/" prefix are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
// Annotations holds free-form, non-identifying descriptive metadata (the
// manifest's metadata.annotations). Unlike Metadata/labels it is never used
// for selection or grouping and carries no reserved keys — purely human/tool
// notes (owner, description, links). Persisted as JSON.
Annotations Metadata `json:"annotations,omitempty" gorm:"serializer:json"`
// ContainerLabels are user-defined Docker labels stamped onto this app's
// container(s)/service at deploy time, for label-driven ecosystem tools
// (Traefik, autoheal, Watchtower, …). Distinct from Metadata (a DB-record
// concern): these land on the real Docker object. Keys under Miabi's reserved
// Docker prefixes (io.miabi.*, miabi.*, com.docker.*) are stripped/rejected so
// user labels can never override or spoof platform system labels. Persisted as
// JSON. Editing them requires a redeploy to take effect (like ports/volumes).
ContainerLabels map[string]string `json:"container_labels,omitempty" gorm:"serializer:json"`
// Runtime config.
Command []string `json:"command,omitempty" gorm:"serializer:json"`
Mounts []AppMount `json:"mounts,omitempty" gorm:"serializer:json"` // attached volumes
Port int `json:"port,omitempty"` // primary container port
MemoryBytes int64 `json:"memory_bytes"` // 0 = unlimited
NanoCPUs int64 `json:"nano_cpus"` // 0 = unlimited; 1 core = 1e9
// GPUCount is the number of whole GPU units this app requests (0 = none). It
// is declarative on the app but only consumes quota while the app is running,
// and is resolved to concrete devices on the app's node at each deploy.
GPUCount int `json:"gpu_count" gorm:"not null;default:0"`
// GPUKind narrows the request to a vendor or model when set ("nvidia",
// "NVIDIA A100-…"); empty = any enabled GPU on the app's node.
GPUKind string `json:"gpu_kind,omitempty"`
// RestartPolicy is the Docker restart policy for the app's container.
// Defaults to unless-stopped (the platform's historical behavior).
RestartPolicy RestartPolicy `json:"restart_policy" gorm:"not null;default:unless-stopped"`
// ImagePullPolicy decides whether a deploy pulls the image. Defaults to
// always (the platform's historical behavior of fetching the tag each deploy).
ImagePullPolicy ImagePullPolicy `json:"image_pull_policy" gorm:"not null;default:always"`
// Healthcheck. Type none disables it; http builds a curl/wget probe against
// HealthcheckHTTPPath on HealthcheckPort (0 = the app's primary port);
// command runs HealthcheckCommand via the shell. When a healthcheck is set,
// a deploy waits for the container to report healthy before going live.
HealthcheckType HealthcheckType `json:"healthcheck_type" gorm:"not null;default:none"`
HealthcheckHTTPPath string `json:"healthcheck_http_path"`
HealthcheckPort int `json:"healthcheck_port"`
HealthcheckCommand string `json:"healthcheck_command"`
HealthcheckIntervalSeconds int `json:"healthcheck_interval_seconds" gorm:"not null;default:30"`
HealthcheckTimeoutSeconds int `json:"healthcheck_timeout_seconds" gorm:"not null;default:5"`
HealthcheckRetries int `json:"healthcheck_retries" gorm:"not null;default:3"`
HealthcheckStartPeriodSeconds int `json:"healthcheck_start_period_seconds" gorm:"not null;default:0"`
// Imported marks an app adopted from a pre-existing (hand-run / compose)
// container rather than created through Miabi. It clears on the first
// native deploy, which recreates the container under Miabi conventions.
Imported bool `json:"imported" gorm:"not null;default:false"`
// ExternalLabel is the stable DNS label used for one-click external access
// (`<label>.<base-domain>`). Generated once on first expose (default
// `<slug>-<token>`) so the public URL survives renames/redeploys.
ExternalLabel string `json:"external_label,omitempty" gorm:"index"`
Status AppStatus `json:"status" gorm:"not null;default:created"`
CurrentReleaseID *uint `json:"current_release_id"`
// RedeployRequired is set when config changes while the app is stopped (the
// running container, if any, is stale). Start/Restart then redeploy instead
// of reusing the old container, and any successful deploy clears it.
RedeployRequired bool `json:"redeploy_required" gorm:"not null;default:false"`
// DeployStrategy is the app's default rollout method, applied when a deploy
// does not specify one. See DeployStrategy constants.
DeployStrategy DeployStrategy `json:"deploy_strategy" gorm:"not null;default:rolling"`
// Canary tuning (used when DeployStrategy is canary or a deploy overrides to
// canary). The platform starts at CanaryInitialWeight and adds
// CanaryStepWeight every CanaryStepIntervalSeconds until it reaches 100%.
CanaryInitialWeight int `json:"canary_initial_weight" gorm:"not null;default:10"`
CanaryStepWeight int `json:"canary_step_weight" gorm:"not null;default:20"`
CanaryStepIntervalSeconds int `json:"canary_step_interval_seconds" gorm:"not null;default:60"`
// Canary live state. When CanaryReleaseID is set, a second (canary) container
// runs alongside the stable release and the reverse-proxy splits traffic:
// CanaryWeight percent goes to the canary, the rest to the stable release.
// Cleared on promote/abort and superseded by any normal deploy.
CanaryReleaseID *uint `json:"canary_release_id,omitempty"`
CanaryWeight int `json:"canary_weight" gorm:"not null;default:0"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
EnvVars []AppEnvVar `json:"env_vars,omitempty" gorm:"foreignKey:ApplicationID"`
Networks []Network `json:"networks,omitempty" gorm:"many2many:application_networks;"`
Ports []AppPort `json:"ports,omitempty" gorm:"foreignKey:ApplicationID"`
Stack *Stack `json:"stack,omitempty" gorm:"foreignKey:StackID;constraint:OnDelete:SET NULL"`
// Credential FKs declared so deleting a referenced registry/git credential
// nulls the pointer here (ON DELETE SET NULL) instead of dangling. The app
// then pulls/clones anonymously until re-linked. Associations not serialized.
Registry *Registry `json:"-" gorm:"foreignKey:RegistryID;constraint:OnDelete:SET NULL"`
GitRepository *GitRepository `json:"-" gorm:"foreignKey:GitRepositoryID;constraint:OnDelete:SET NULL"`
}
Application is a deployable workload owned by a workspace.
func (*Application) ImageRef ¶
func (a *Application) ImageRef(tagOverride string) string
ImageRef composes the effective pull reference from Image and Tag. When tagOverride is non-empty it takes precedence over the app's Tag. If the image already carries a tag or digest, it is used verbatim; otherwise the tag (or "latest") is appended.
type AuditLog ¶
type AuditLog struct {
ID uint `json:"id" gorm:"primaryKey"`
ActorID *uint `json:"actor_id" gorm:"index"`
WorkspaceID *uint `json:"workspace_id" gorm:"index"`
Action string `json:"action" gorm:"index;not null"`
TargetType string `json:"target_type"`
TargetID string `json:"target_id"`
IPAddress string `json:"ip_address"`
Metadata map[string]any `json:"metadata,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
}
AuditLog is an append-only record of a mutating action.
type Backup ¶
type Backup struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
DatabaseID uint `json:"database_id" gorm:"index;not null"`
Engine DBEngine `json:"engine"`
ServerID uint `json:"server_id" gorm:"index;not null;default:0"` // node the backup ran on
Status BackupStatus `json:"status" gorm:"not null;default:pending"`
Trigger string `json:"trigger"` // manual | scheduled
Destination string `json:"destination"` // local | s3
VolumeName string `json:"volume_name,omitempty"` // local destination
S3Bucket string `json:"s3_bucket,omitempty"` // s3 destination (reference)
S3Path string `json:"s3_path,omitempty"`
Filename string `json:"filename,omitempty"`
SizeBytes int64 `json:"size_bytes"`
// Logs is a bounded tail of the backup output for instant display; the full
// log lives in the log store at LogRef once the run is terminal (see
// plans/log-storage.md). LogRef is empty when the store is disabled or the
// row predates externalization — readers fall back to this tail.
Logs string `json:"logs,omitempty" gorm:"type:text"`
LogRef string `json:"log_ref,omitempty"`
LogBytes int64 `json:"log_bytes,omitempty"`
LogLines int `json:"log_lines,omitempty"`
LogTruncated bool `json:"log_truncated,omitempty"`
Error string `json:"error,omitempty" gorm:"type:text"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `json:"created_at"`
}
Backup is a single backup run of a managed database.
type BackupSchedule ¶
type BackupSchedule struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
DatabaseID uint `json:"database_id" gorm:"index;not null"`
Cron string `json:"cron" gorm:"not null"`
Destination string `json:"destination" gorm:"not null;default:local"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
// Retention, applied after each scheduled run. 0 = unlimited.
MaxBackups int `json:"max_backups" gorm:"not null;default:0"` // keep at most N most-recent backups
RetentionDays int `json:"retention_days" gorm:"not null;default:0"` // delete backups older than N days
S3Endpoint string `json:"s3_endpoint,omitempty"`
S3Bucket string `json:"s3_bucket,omitempty"`
S3Region string `json:"s3_region,omitempty"`
S3AccessKey string `json:"s3_access_key,omitempty"`
S3SecretKeyEnc string `json:"-" gorm:"column:s3_secret_key_enc"` // encrypted
S3Path string `json:"s3_path,omitempty"`
S3UseSSL bool `json:"s3_use_ssl"`
S3ForcePathStyle bool `json:"s3_force_path_style"`
LastRunAt *time.Time `json:"last_run_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
BackupSchedule runs backups of a database on a cron schedule. When Destination is "s3", the S3* fields hold the target (secret key encrypted).
type BackupStatus ¶
type BackupStatus string
BackupStatus is the state of a backup run.
const ( BackupPending BackupStatus = "pending" BackupRunning BackupStatus = "running" BackupCompleted BackupStatus = "completed" BackupFailed BackupStatus = "failed" )
type Certificate ¶
type Certificate struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_cert_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_cert_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
CertPEM string `json:"-" gorm:"type:text"` // leaf + intermediates (PEM)
KeyEnc string `json:"-" gorm:"type:text"` // private key, encrypted at rest
// Parsed metadata (populated on import / replace / issue).
CommonName string `json:"common_name"`
DNSNames []string `json:"dns_names" gorm:"serializer:json"` // SANs, for host auto-match
Issuer string `json:"issuer"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
SerialHex string `json:"serial_hex"`
// Managed (ACME) fields; apply only to "acme" certs (Source defaults to "imported").
Source string `json:"source" gorm:"not null;default:imported"` // imported | acme
DNSProviderID *uint `json:"dns_provider_id,omitempty" gorm:"index"` // provider used to issue
AutoRenew bool `json:"auto_renew" gorm:"not null;default:false"`
Status string `json:"status" gorm:"not null;default:active"` // active | issuing | failed
LastError string `json:"last_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Certificate is a workspace-scoped TLS certificate. A route with TLSMode=custom references it by CertificateID; the PEM/key are sourced server-side at render time. The leaf + chain (CertPEM) is public, but the private key (KeyEnc) is encrypted at rest and never returned. Metadata is parsed from the leaf.
type CronJob ¶
type CronJob struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
ApplicationID uint `json:"application_id" gorm:"index;not null"`
Name string `json:"name" gorm:"not null"`
Schedule string `json:"schedule" gorm:"not null"` // cron expression (UTC)
// AppName is the owning application's name (transient; populated on read).
AppName string `json:"app_name,omitempty" gorm:"-"`
Command []string `json:"command" gorm:"serializer:json"`
Entrypoint []string `json:"entrypoint,omitempty" gorm:"serializer:json"`
TimeoutSecs int `json:"timeout_secs" gorm:"not null;default:0"`
// Image optionally overrides the app's active-release image for spawned jobs
// (blank = run the app's current image). RegistryID authenticates its pull.
Image string `json:"image,omitempty"`
RegistryID *uint `json:"registry_id,omitempty"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
ConcurrencyPolicy string `json:"concurrency_policy" gorm:"not null;default:allow"`
HistoryLimit int `json:"history_limit" gorm:"not null;default:0"` // keep last N spawned jobs (0 = default)
LastRunAt *time.Time `json:"last_run_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
CronJob is a schedule that spawns Jobs on a cron expression — the command it carries is the template each spawned Job is created from. Schedules are evaluated in UTC; missed ticks (control plane down) are not backfilled.
type CustomRole ¶
type CustomRole struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID *uint `json:"workspace_id" gorm:"index"`
Name string `json:"name" gorm:"not null"`
BaseRole WorkspaceRole `json:"base_role" gorm:"not null"`
Permissions []string `json:"permissions" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
CustomRole is an admin-defined permission set assigned to workspace members (Enterprise; gated on the custom_roles entitlement for writes). BaseRole gives a rank so the legacy rank-based RequireRole still resolves sanely for a member on a custom role; the Permissions list drives the fine-grained RequirePermission checks. The table is empty in Community.
func (*CustomRole) PermissionSet ¶
func (r *CustomRole) PermissionSet() map[Permission]bool
PermissionSet returns the role's permissions as a lookup set.
type DBEngine ¶
type DBEngine string
DBEngine is a supported managed-database engine.
const ( DBEnginePostgres DBEngine = "postgres" DBEngineMySQL DBEngine = "mysql" DBEngineMariaDB DBEngine = "mariadb" DBEngineRedis DBEngine = "redis" DBEngineMongoDB DBEngine = "mongodb" // DBEngineLibSQL is a single-database libSQL (sqld) server. Unlike the SQL // engines it hosts exactly one database (no user-managed logical databases), // has no CLI client (clients speak HTTP/Hrana), and authenticates with a JWT. DBEngineLibSQL DBEngine = "libsql" )
type DBStatus ¶
type DBStatus string
DBStatus is the lifecycle state of a managed database.
const ( DBStatusProvisioning DBStatus = "provisioning" DBStatusRunning DBStatus = "running" DBStatusStopped DBStatus = "stopped" DBStatusFailed DBStatus = "failed" // DBStatusUpgrading marks an instance whose engine version is being upgraded. // Lifecycle actions (start/stop/restart/delete) are refused while in this // state; the transient Upgrade* fields carry live progress. DBStatusUpgrading DBStatus = "upgrading" )
type DNSProvider ¶
type DNSProvider struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_dnsprov_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_dnsprov_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
Type string `json:"type" gorm:"not null"` // cloudflare | route53 | digitalocean
// CredentialsEnc is crypto.Encrypt(JSON-of-credentials); write-only — never
// serialized to clients (json:"-").
CredentialsEnc string `json:"-" gorm:"type:text"`
// Status / LastError record the last connection test or reconcile result.
Status string `json:"status" gorm:"not null;default:ok"` // ok | error
LastError string `json:"last_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
DNSProvider is a workspace's connection to a DNS host. Credentials are an opaque, encrypted JSON blob whose shape depends on Type (a Cloudflare API token; Route 53 access key/secret/region; a DigitalOcean token). The blob is encrypted at rest (crypto.Encrypt) and never returned by the API after create.
type DNSRecord ¶
type DNSRecord struct {
ID uint `json:"id" gorm:"primaryKey"`
DomainID uint `json:"domain_id" gorm:"index:idx_dnsrec_domain_key,unique;not null"`
// Name+Type form the managed key within a domain (one ledgered record per
// name+type). Name is a FQDN.
Name string `json:"name" gorm:"index:idx_dnsrec_domain_key,unique;not null"`
Type string `json:"type" gorm:"index:idx_dnsrec_domain_key,unique;not null"` // TXT | A | AAAA | CNAME
Value string `json:"value"`
Purpose string `json:"purpose" gorm:"not null"` // verification | app-address
// AppID is set for app-address records so they are cleaned up when the app /
// route is removed.
AppID *uint `json:"app_id,omitempty" gorm:"index"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
DNSRecord is the ledger of DNS records Miabi created through a connected provider, so reconcile and cleanup only ever touch records Miabi owns — never a user's other records (the same prune-only-managed safety the GitOps engine uses). A record at a managed name that is NOT in this ledger is treated as a conflict, never overwritten.
type Database ¶
type Database struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
InstanceID uint `json:"instance_id" gorm:"index:idx_db_instance_name,unique;not null"`
Name string `json:"name" gorm:"index:idx_db_instance_name,unique;not null"` // db name on the server
Username string `json:"username" gorm:"not null"`
PasswordEnc string `json:"-" gorm:"column:password_enc"` // encrypted
Status DBStatus `json:"status" gorm:"not null;default:provisioning"`
ApplicationID *uint `json:"application_id,omitempty" gorm:"index"` // optional owner
// Application declares the FK so deleting the owning app detaches this
// database (ON DELETE SET NULL) instead of dangling. Not serialized.
Application *Application `json:"-" gorm:"foreignKey:ApplicationID;constraint:OnDelete:SET NULL"`
// EnvPrefix namespaces the connection env vars injected into the owning app
// (e.g. "ANALYTICS" -> ANALYTICS_DATABASE_URL); empty = unprefixed
// (DATABASE_URL, DB_*). Lets one app hold several databases without clobbering.
EnvPrefix string `json:"env_prefix,omitempty"`
// SizeBytes is the database's on-disk size, refreshed by a sync.
SizeBytes int64 `json:"size_bytes"`
SizeSyncedAt *time.Time `json:"size_synced_at"`
// Metadata holds free-form labels; "miabi.io/" keys are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Database is a logical database hosted on a DatabaseInstance, with its own dedicated user (privileges scoped to this database). The password is encrypted at rest. Optionally owned by an application.
type DatabaseInstance ¶
type DatabaseInstance struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_dbinst_workspace_name,unique;not null"`
// Name is the unique, URL/CLI/docker handle (lowercase [a-z0-9-]) scoped to the
// workspace. Renamed from the former "slug".
Name string `json:"name" gorm:"index:idx_dbinst_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI. Renamed from "name".
DisplayName string `json:"display_name"`
Engine DBEngine `json:"engine" gorm:"not null"`
Version string `json:"version" gorm:"not null"`
Status DBStatus `json:"status" gorm:"not null;default:provisioning"`
// ServerID is the node this instance runs on (0 = local control-plane node).
ServerID uint `json:"server_id" gorm:"index;not null;default:0"`
// ServerName is the display name of the node (transient; populated on read so
// the UI can show where the instance lives). Empty if the node is unknown.
ServerName string `json:"server_name,omitempty" gorm:"-"`
ContainerID string `json:"container_id,omitempty"`
// Image is the resolved server image ref (repo:tag), pinned at provision time
// from the deployment-config catalog. Empty on legacy rows (falls back to the
// engine default).
Image string `json:"image,omitempty"`
VolumeName string `json:"volume_name,omitempty"`
// VolumeSizeBytes is the declared capacity of the instance's data volume in
// bytes (0 = unspecified/unlimited), distinct from the measured SizeBytes
// below. Recorded at provision time for quota accounting.
VolumeSizeBytes int64 `json:"volume_size_bytes" gorm:"not null;default:0"`
// MountPath is the in-container data directory the volume is mounted at
// (transient; populated on read from the engine spec).
MountPath string `json:"mount_path,omitempty" gorm:"-"`
Host string `json:"host"` // in-network DNS alias
Port int `json:"port"`
// NetworkName is the Docker network the instance's container joins — the
// workspace's default network, shared with the workspace's applications so
// they reach the database by its alias. The gateway network is reserved for
// exposed (routed) applications. Empty on legacy rows (fall back to gateway).
NetworkName string `json:"network_name,omitempty"`
// Admin (superuser/root) credentials for the server. For Redis this is the
// requirepass password (AdminUser empty).
AdminUser string `json:"admin_user"`
AdminPasswordEnc string `json:"-" gorm:"column:admin_password_enc"` // encrypted
// JWTPrivateKeyEnc is the encrypted Ed25519 private key used to mint client
// auth tokens for a libSQL instance. sqld is started with the matching public
// key (SQLD_AUTH_JWT_KEY), derived from this private key at bring-up. Empty for
// every other engine.
JWTPrivateKeyEnc string `json:"-" gorm:"column:jwt_private_key_enc"` // encrypted
// SizeBytes is the instance's on-disk size (sum of logical DBs for SQL, used
// memory for Redis), refreshed by a sync. SizeSyncedAt is when it was last
// computed.
SizeBytes int64 `json:"size_bytes"`
SizeSyncedAt *time.Time `json:"size_synced_at"`
// Metadata holds free-form labels; "miabi.io/" keys are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
// Annotations holds free-form, non-identifying descriptive metadata (the
// manifest's metadata.annotations); no reserved keys. Persisted as JSON.
Annotations Metadata `json:"annotations,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Upgrade carries live progress for a version upgrade. Persisted (as JSON) so
// it is visible across the API server and the worker that runs the job, and
// survives a restart. Nil when no upgrade is in flight; on failure it lingers
// with Phase="failed" until the next upgrade attempt clears it.
Upgrade *UpgradeProgress `json:"upgrade,omitempty" gorm:"serializer:json"`
Databases []Database `json:"databases,omitempty" gorm:"foreignKey:InstanceID"`
// Networks are the Docker networks the instance's container joins. The
// workspace's default network is always attached (it carries the alias apps
// reach the instance by); the user may attach additional custom networks.
Networks []Network `json:"networks,omitempty" gorm:"many2many:database_instance_networks;"`
}
DatabaseInstance is a workspace-owned database server running as a container (PostgreSQL/MySQL/MariaDB/Redis). A single instance hosts one or more logical Databases (SQL engines), so small deployments can share one server across apps instead of one container per database. Admin credentials are used to create/drop the logical databases and are encrypted at rest.
func (*DatabaseInstance) NetworkNames ¶
func (i *DatabaseInstance) NetworkNames(fallback string) []string
NetworkNames returns every Docker network the instance's container joins: the pinned primary network first (the alias-bearing default), then each other attached network. Helper jobs (DDL, size probes, backup, restore) must join this full set — not just the primary — so they always share a network with the instance and can resolve it by name; attaching to only one risks landing on a network the instance is not on. fallback (the gateway network) is used when no primary is pinned (legacy rows provisioned before per-workspace networking). The result is de-duplicated and never empty.
func (*DatabaseInstance) SupportsLogicalDatabases ¶
func (i *DatabaseInstance) SupportsLogicalDatabases() bool
SupportsLogicalDatabases reports whether the instance's engine can host named per-app databases with their own users.
type DeployStrategy ¶
type DeployStrategy string
DeployStrategy is how a new release replaces the running one.
const ( // DeployRecreate stops the current container before starting the new one // (brief downtime; use when two versions cannot run at once). DeployRecreate DeployStrategy = "recreate" // DeployRolling starts the new container, waits for health, then retires the // old one (zero-downtime for a single replica). Default. DeployRolling DeployStrategy = "rolling" // DeployCanary runs the new release alongside the stable one and shifts // weighted traffic to it progressively (platform-driven). DeployCanary DeployStrategy = "canary" )
type Deployment ¶
type Deployment struct {
ID uint `json:"id" gorm:"primaryKey"`
// Number is the per-application sequential deployment number (1, 2, 3…),
// independent of the global ID. It mirrors Release.Version: stable and
// user-friendly for managing an app's deploy history (the first deploy is #1,
// the second #2, …). Assigned automatically in BeforeCreate; the composite
// unique index keeps it gap-free per app and prevents duplicates.
Number int `json:"number" gorm:"index:idx_deploy_app_number,unique;not null;default:0"`
ApplicationID uint `json:"application_id" gorm:"index:idx_deploy_app_number,unique;index;not null"`
Status DeploymentStatus `json:"status" gorm:"not null;default:pending"`
Image string `json:"image"`
Trigger string `json:"trigger"` // manual | rollback | auto | pipeline
Strategy DeployStrategy `json:"strategy" gorm:"not null;default:rolling"` // rollout method for this deploy
// Commit pins the source revision this deployment was built from. Set by the
// pipeline runner so a deploy reproduces the exact commit the run captured,
// even if the app's branch advanced after the run started.
Commit string `json:"commit,omitempty"`
// ImageID links a deployment to a prebuilt Image (built by a pipeline run on
// this node). When set and the image is present locally the deploy worker
// runs it directly — no rebuild, no pull.
ImageID *uint `json:"image_id,omitempty"`
// RegistryID is the registry credential used for this deploy (snapshot of
// the app's selection, allowing per-deploy override). nil = anonymous.
RegistryID *uint `json:"registry_id,omitempty"`
// RunnerID records which runner built the image this deployment rolls out
// (provenance / "built on runner X"); nil for images built by the internal
// runner or pulled directly.
RunnerID *uint `json:"runner_id,omitempty"`
ContainerID string `json:"container_id,omitempty"`
// Logs is a bounded tail of the build/deploy output for instant display; the
// full log lives in the log store at LogRef once the deployment is terminal
// (see plans/log-storage.md). LogRef is empty when the store is disabled or
// the row predates externalization — readers fall back to this tail.
Logs string `json:"logs,omitempty" gorm:"type:text"`
LogRef string `json:"log_ref,omitempty"`
LogBytes int64 `json:"log_bytes,omitempty"`
LogLines int `json:"log_lines,omitempty"`
LogTruncated bool `json:"log_truncated,omitempty"`
Error string `json:"error,omitempty" gorm:"type:text"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `json:"created_at"`
// Current is a transient flag (not persisted) marking the deployment whose
// release is the application's active one — i.e. what is live right now.
Current bool `json:"current" gorm:"-"`
}
Deployment is a single attempt to deploy an application.
func (*Deployment) BeforeCreate ¶
func (d *Deployment) BeforeCreate(tx *gorm.DB) error
BeforeCreate assigns the per-application sequential Number (MAX+1 scoped to the app), mirroring how Release.Version is allocated. Running inside the insert's transaction keeps every creation path (deploy, redeploy, pipeline, import) consistent without each call site computing the number itself. An explicit Number (e.g. a backfill) is left untouched.
type DeploymentStatus ¶
type DeploymentStatus string
DeploymentStatus is the state of a single deploy attempt.
const ( DeploymentPending DeploymentStatus = "pending" DeploymentBuilding DeploymentStatus = "building" DeploymentDeploying DeploymentStatus = "deploying" // DeploymentCanary is a non-terminal state: the canary container is live and // serving a weighted share of traffic while the rollout progresses. It becomes // terminal when the canary is promoted/superseded (succeeded) or aborted // (failed), so the deployment's log stream stays open for the whole rollout. DeploymentCanary DeploymentStatus = "canary" // DeploymentSucceeded is the terminal success state of a deploy attempt. // Whether the app is currently live is a property of the active Release, not // of a historical deployment. DeploymentSucceeded DeploymentStatus = "succeeded" DeploymentFailed DeploymentStatus = "failed" )
func (DeploymentStatus) IsTerminal ¶
func (s DeploymentStatus) IsTerminal() bool
IsTerminal reports whether the deployment has reached a final state.
type Domain ¶
type Domain struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_domain_workspace_name,unique;not null"`
Name string `json:"name" gorm:"index:idx_domain_workspace_name,unique;not null"` // e.g. example.com
// TLSMode is the default certificate policy for routes under this domain.
TLSMode DomainTLSMode `json:"tls_mode" gorm:"not null;default:acme"`
// Wildcard marks the domain as covering *.name as well as name.
Wildcard bool `json:"wildcard" gorm:"not null;default:false"`
// Verified records DNS-proven ownership. VerificationToken is published by the
// user as a TXT record; it is not a secret (it is meant to be public).
Verified bool `json:"verified" gorm:"not null;default:false"`
VerifiedAt *time.Time `json:"verified_at,omitempty"`
VerificationToken string `json:"verification_token" gorm:"not null"`
// VerificationCheckedAt records the last time ownership was checked (whether or
// not it succeeded), and VerificationError carries the last failure reason.
// Together they give the UI an observable verification history and let the
// drift cron count consecutive misses before un-verifying a domain.
VerificationCheckedAt *time.Time `json:"verification_checked_at,omitempty"`
VerificationError string `json:"verification_error,omitempty"`
// VerificationMisses counts consecutive failed re-verifications of an
// already-verified domain; the drift cron flips Verified off once it crosses a
// threshold, absorbing transient DNS blips.
VerificationMisses int `json:"-" gorm:"not null;default:0"`
// Banned blocks a domain platform-wide: a banned domain is never served by the
// gateway (its routes are forced offline) and cannot be verified, regardless of
// ownership or workspace privilege. Set by a platform admin (e.g. for abuse).
Banned bool `json:"banned" gorm:"not null;default:false"`
BannedAt *time.Time `json:"banned_at,omitempty"`
BanReason string `json:"ban_reason,omitempty"`
// DNSProviderID optionally links the domain to a connected DNS provider so
// Miabi automates the ownership TXT (and, later, app A/AAAA) records. nil =
// manual: the user adds DNS records by hand (today's copy-paste flow).
DNSProviderID *uint `json:"dns_provider_id,omitempty" gorm:"index"`
// DNSProvider declares the FK so deleting the provider nulls this link
// (ON DELETE SET NULL) — the domain reverts to manual DNS — instead of
// dangling. Association not serialized.
DNSProvider *DNSProvider `json:"-" gorm:"foreignKey:DNSProviderID;constraint:OnDelete:SET NULL"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Domain is a hostname (or zone) a workspace owns. It tracks DNS-verified ownership and the default TLS policy; routes bind hostnames that resolve under a verified domain. This is the first-class owned-hostname resource — distinct from the declarative Route kind, which is an HTTP routing rule.
func (*Domain) ChallengeHost ¶
ChallengeHost is the DNS name where the ownership TXT record must be added.
func (*Domain) ChallengeValue ¶
ChallengeValue is the expected TXT record value proving ownership.
func (*Domain) Status ¶
func (d *Domain) Status() DomainStatus
Status derives the domain's presentational state for the UI. A ban takes precedence over verification, since it overrides serving entirely.
type DomainStatus ¶
type DomainStatus string
DomainStatus is a presentational verification state derived from the domain's verification fields (not stored): "verified", "failed" (a check ran and did not prove ownership), or "pending" (no successful check yet).
const ( DomainStatusPending DomainStatus = "pending" DomainStatusVerified DomainStatus = "verified" DomainStatusFailed DomainStatus = "failed" DomainStatusBanned DomainStatus = "banned" )
type DomainTLSMode ¶
type DomainTLSMode string
DomainTLSMode is the default certificate policy for routes served under a domain.
const ( // DomainTLSACME serves certificates from Goma's ACME (Let's Encrypt) issuer. DomainTLSACME DomainTLSMode = "acme" // DomainTLSCustom expects a user-supplied certificate. DomainTLSCustom DomainTLSMode = "custom" )
type Environment ¶
type Environment struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_env_ws_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace (e.g. "prod").
Name string `json:"name" gorm:"index:idx_env_ws_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
Description string `json:"description,omitempty"`
// Rank orders environments for promotion flow (lower promotes into higher).
Rank int `json:"rank" gorm:"not null;default:0"`
// RequiredApprovals gates promotion into this environment (0 = no gate).
RequiredApprovals int `json:"required_approvals" gorm:"not null;default:0"`
// GitSourceID optionally binds the environment to a GitOps source/folder.
GitSourceID *uint `json:"git_source_id,omitempty" gorm:"index"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Environment is a promotion stage within a workspace (dev → staging → prod). A Release is promoted by updating the target environment's desired state — a Git commit (GitOps) or an API write — so promotion is auditable and reconciled.
type GPUDevice ¶ added in v1.2.0
type GPUDevice struct {
ID uint `json:"id" gorm:"primaryKey"`
ServerID uint `json:"server_id" gorm:"index;not null"` // the node it lives on
// UUID is the stable identity reported by the node agent (nvidia-smi -q -x),
// e.g. "GPU-3f2c...". It is the join key across re-inventories.
UUID string `json:"uuid" gorm:"uniqueIndex;not null"`
Index int `json:"index"` // host-local device index at last inventory (informational)
Vendor GPUVendor `json:"vendor" gorm:"not null;default:nvidia"`
Model string `json:"model"` // "NVIDIA A100-SXM4-40GB"
MemoryMB int `json:"memory_mb"` // total framebuffer memory
// Admin policy. A device is not offered to any workload until Enabled — a
// newly discovered card always arrives disabled (fail closed).
Enabled bool `json:"enabled" gorm:"not null;default:false"`
// Dedicated (false): reserved for one app at a time (training). Fleet-wide
// exclusivity enforcement for dedicated cards lands in a later phase.
Shared bool `json:"shared" gorm:"not null;default:true"`
// LastSeenAt is stamped on every inventory scan that observes the device. A
// device that drops out of a scan is kept (not deleted) so history and an
// app's GPUKind reference survive a transient probe failure or a reboot
// mid-scan; staleness is derived from an old LastSeenAt.
LastSeenAt *time.Time `json:"last_seen_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
GPUDevice is one physical GPU discovered on a node's Docker host. A node advertises the GPUs it has (via the inventory probe); the platform admin controls whether each is offered to workloads. Rows are owned by a Server and keyed for re-inventory by the stable GPU UUID reported by nvidia-smi — never the host-local index, which can change across reboots.
Devices get their own table (not a JSON blob on Server) because each is individually addressable: the admin enables/disables and marks shared or dedicated per card, and a device is the unit an app is scheduled onto. Same reasoning that gave network subnets their own rows.
type GPUVendor ¶ added in v1.2.0
type GPUVendor string
GPUVendor is the vendor of a physical GPU. Only "nvidia" is supported in v1; "amd" is reserved for a later ROCm phase.
type GatewayUpdateProgress ¶
type GatewayUpdateProgress struct {
FromImage string `json:"from_image"`
ToImage string `json:"to_image"`
// Phase: queued | pulling | testing | observing | promoting | verifying |
// done | failed.
Phase string `json:"phase"`
Error string `json:"error,omitempty"`
}
GatewayUpdateProgress is the live state of a safe edge-gateway update: the new image is first started as a throwaway test container with the same config and volumes, observed for a grace period, then promoted to replace the live gateway. The admin watches these phases on the node detail page. Mirrors UpgradeProgress (database version upgrades).
type GitAuthType ¶
type GitAuthType string
GitAuthType is how an application authenticates to a Git repository.
const ( // GitAuthPublic is an anonymous (public) repository — no credentials. GitAuthPublic GitAuthType = "public" // GitAuthToken uses HTTPS basic auth (username + personal access token). GitAuthToken GitAuthType = "token" // GitAuthSSH uses an SSH private key against an ssh:// or git@ URL. GitAuthSSH GitAuthType = "ssh" )
type GitRepository ¶
type GitRepository struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_gitrepo_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_gitrepo_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
URL string `json:"url" gorm:"not null"`
AuthType GitAuthType `json:"auth_type" gorm:"not null;default:token"`
// Username for HTTPS basic auth. Empty defaults to a provider-friendly value
// ("x-access-token") at clone time.
Username string `json:"username"`
// Secret holds the access token (token auth) or SSH private key (ssh auth),
// encrypted at rest.
Secret string `json:"-" gorm:"not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// HasSecret is a transient flag for responses (never persisted).
HasSecret bool `json:"has_secret" gorm:"-"`
}
GitRepository is a stored Git credential used to clone private repositories at build time. The Secret (token or SSH private key) is encrypted at rest and never serialized.
type GitSource ¶
type GitSource struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_gitsource_ws_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_gitsource_ws_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
RepoURL string `json:"repo_url" gorm:"not null"`
Ref string `json:"ref" gorm:"not null;default:main"` // branch, tag or commit
Path string `json:"path" gorm:"not null;default:."` // manifest subdirectory
// GitRepositoryID optionally references stored credentials (token/SSH) for a
// private repository. nil means the repo is public.
GitRepositoryID *uint `json:"git_repository_id,omitempty" gorm:"index"`
SyncPolicy GitSyncPolicy `json:"sync_policy" gorm:"not null;default:manual"`
// Prune deletes managed resources that disappear from Git (opt-in for safety).
Prune bool `json:"prune" gorm:"not null;default:false"`
// SelfHeal re-applies when the live state drifts from Git.
SelfHeal bool `json:"self_heal" gorm:"not null;default:false"`
// AllowEmpty permits an empty manifest set to prune ALL managed resources
// (intentional teardown). Off by default: an empty/zero-resource result is
// otherwise treated as an error, never a "delete everything" instruction, so
// a wrong path or a wiped repo can't tear down a deployment. A missing path is
// always an error regardless. Only meaningful together with Prune.
AllowEmpty bool `json:"allow_empty" gorm:"not null;default:false"`
// WebhookSecret authenticates inbound provider push webhooks. Never serialized.
WebhookSecret string `json:"-" gorm:"not null"`
Status GitSourceStatus `json:"status" gorm:"not null;default:unknown"`
Message string `json:"message,omitempty" gorm:"type:text"`
LastSyncedCommit string `json:"last_synced_commit,omitempty"`
// LastSyncedAuthor/Subject record who authored the synced commit and its
// subject line, for display on the detail page.
LastSyncedAuthor string `json:"last_synced_author,omitempty"`
LastSyncedSubject string `json:"last_synced_subject,omitempty"`
LastSyncedAt *time.Time `json:"last_synced_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
GitSource binds a Git repository path of miabi.io/v1 manifests to a workspace as a continuously-reconciled desired-state source. It is the GitOps half of the declarative model: Git → (sync) → Postgres desired → (existing reconciler) → Docker.
type GitSourceStatus ¶
type GitSourceStatus string
GitSourceStatus is the aggregate sync state of a GitSource.
const ( GitSourceUnknown GitSourceStatus = "unknown" GitSourceSynced GitSourceStatus = "synced" GitSourceOutOfSync GitSourceStatus = "out_of_sync" GitSourceProgressing GitSourceStatus = "progressing" GitSourceError GitSourceStatus = "error" )
type GitSyncPolicy ¶
type GitSyncPolicy string
GitSyncPolicy controls when a GitSource reconciles.
const ( // GitSyncManual reconciles only on an explicit Sync (or webhook) request. GitSyncManual GitSyncPolicy = "manual" // GitSyncAuto reconciles on the polling sweep and on webhook push. GitSyncAuto GitSyncPolicy = "auto" )
type HealthcheckType ¶
type HealthcheckType string
HealthcheckType is how an application's container health is probed.
const ( HealthcheckNone HealthcheckType = "none" // no healthcheck HealthcheckHTTP HealthcheckType = "http" // HTTP GET against a path/port HealthcheckCommand HealthcheckType = "command" // shell command (CMD-SHELL) )
type Image ¶
type Image struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_image_ws_digest,unique;not null"`
Repository string `json:"repository" gorm:"not null"`
Digest string `json:"digest" gorm:"index:idx_image_ws_digest,unique;not null"` // sha256:…
Tag string `json:"tag,omitempty"`
SizeBytes int64 `json:"size_bytes"`
// Provenance.
PipelineRunID *uint `json:"pipeline_run_id,omitempty" gorm:"index"`
ApplicationID *uint `json:"application_id,omitempty" gorm:"index"`
Commit string `json:"commit,omitempty"`
Runner string `json:"runner,omitempty"`
BuiltAt *time.Time `json:"built_at,omitempty"`
// SBOM / Signature attach supply-chain metadata. Never serialized.
SBOM string `json:"-" gorm:"type:text"`
Signature string `json:"-" gorm:"type:text"`
CreatedAt time.Time `json:"created_at"`
}
Image is a built container artifact with provenance. Deploy-by-digest records exactly what ran; the catalog/GC reasons about retention without ever collecting a digest a live deployment or pinned release references.
type ImagePullPolicy ¶
type ImagePullPolicy string
ImagePullPolicy decides whether a deploy pulls the app's image from the registry. The platform default is "always" (a redeploy fetches the current tag); "if-not-present" reuses a locally cached image; "never" requires it to be present already (air-gapped / locally built). Digest-pinned refs are immutable, so they are never re-pulled when already present regardless of policy.
const ( PullAlways ImagePullPolicy = "always" // always pull (default) PullIfNotPresent ImagePullPolicy = "if-not-present" // pull only when the image is absent locally PullNever ImagePullPolicy = "never" // never pull; fail if the image is absent )
type InvitationStatus ¶
type InvitationStatus string
InvitationStatus is the lifecycle state of a workspace invitation.
const ( InvitationStatusPending InvitationStatus = "pending" InvitationStatusAccepted InvitationStatus = "accepted" InvitationStatusRevoked InvitationStatus = "revoked" )
type Job ¶
type Job struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
ApplicationID uint `json:"application_id" gorm:"index;not null"`
// ServerID is the node the job ran on (0 = local), copied from the app.
ServerID uint `json:"server_id" gorm:"not null;default:0"`
// CronJobID links runs spawned by a CronJob to their schedule.
CronJobID *uint `json:"cronjob_id,omitempty" gorm:"index"`
// AppName is the owning application's name (transient; populated on read for
// the workspace-level Jobs view).
AppName string `json:"app_name,omitempty" gorm:"-"`
Name string `json:"name"`
Command []string `json:"command" gorm:"serializer:json"`
Entrypoint []string `json:"entrypoint,omitempty" gorm:"serializer:json"`
Image string `json:"image"` // resolved image actually run
// RegistryID authenticates the image pull for a custom image (nil = the app's
// registry, or anonymous). Pull marks that the image must be pulled before the
// run (set for a custom image; the app's active-release image is already
// present on its node, and git-built images are local-only).
RegistryID *uint `json:"registry_id,omitempty"`
Pull bool `json:"pull"`
Status JobStatus `json:"status" gorm:"not null;default:pending"`
ExitCode *int `json:"exit_code,omitempty"`
ContainerID string `json:"container_id,omitempty"`
// Logs is a bounded tail of the run's output for instant display; the full
// log lives in the log store at LogRef once the run is terminal (see
// plans/log-storage.md). LogRef is empty when the store is disabled or the
// row predates externalization — readers then fall back to this tail.
Logs string `json:"logs,omitempty" gorm:"type:text"`
LogRef string `json:"log_ref,omitempty"`
LogBytes int64 `json:"log_bytes,omitempty"`
LogLines int `json:"log_lines,omitempty"`
LogTruncated bool `json:"log_truncated,omitempty"`
Error string `json:"error,omitempty" gorm:"type:text"`
TimeoutSecs int `json:"timeout_secs" gorm:"not null;default:0"`
TriggeredByID *uint `json:"triggered_by_id,omitempty"`
Source string `json:"source" gorm:"not null;default:manual"` // manual | api | scheduled
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Job is a single one-off command run in an application's runtime context (same image, env, networks, volumes, node, and resource limits as the app). The command is captured for history/audit; secrets are resolved into the container env at run time and never persisted on the row.
type JobStatus ¶
type JobStatus string
JobStatus is the lifecycle state of a one-off Job run.
func (JobStatus) IsTerminal ¶
IsTerminal reports whether the job has reached a final state.
type LDAPConfig ¶
type LDAPConfig struct {
ID uint `json:"id" gorm:"primaryKey"`
OrganizationID *uint `json:"organization_id" gorm:"index"`
// Name is the globally unique handle (slug); DisplayName is the free-text label.
Name string `json:"name" gorm:"uniqueIndex;not null"`
DisplayName string `json:"display_name"`
// Connection.
Host string `json:"host" gorm:"not null"`
Port int `json:"port" gorm:"not null;default:389"`
TLSMode string `json:"tls_mode" gorm:"not null;default:'starttls'"` // none | starttls | ldaps
CACertPEM string `json:"ca_cert_pem" gorm:"type:text"` // optional private-CA trust
InsecureSkipTLS bool `json:"insecure_skip_tls" gorm:"not null;default:false"`
TimeoutSeconds int `json:"timeout_seconds" gorm:"not null;default:10"`
// Bind — the service account used to search for the user entry before
// re-binding as the user to verify the password.
BindDN string `json:"bind_dn"`
// BindPasswordEnc is the service-account password, encrypted at rest (crypto
// package). Never serialized; BindPasswordSet exposes only whether it is set.
BindPasswordEnc string `json:"-" gorm:"type:text"`
// User search.
UserBaseDN string `json:"user_base_dn"`
UserFilter string `json:"user_filter"` // %s = escaped identifier, e.g. (sAMAccountName=%s) or (uid=%s)
AttrEmail string `json:"attr_email"` // mail | userPrincipalName (blank → "mail")
AttrName string `json:"attr_name"` // displayName | cn (blank → "displayName")
AttrUsername string `json:"attr_username"` // sAMAccountName | uid → User.Username
// Group resolution (optional; drives access via LDAPGroupMapping).
GroupBaseDN string `json:"group_base_dn"`
GroupFilter string `json:"group_filter"` // %s = user DN, e.g. (member=%s); blank → use MemberAttr
MemberAttr string `json:"member_attr"` // memberOf (read from the user entry) — AD default
NestedGroups bool `json:"nested_groups"` // AD: expand nested groups (LDAP_MATCHING_RULE_IN_CHAIN)
Enabled bool `json:"enabled" gorm:"not null;default:true"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Mappings are loaded on demand (not embedded in list responses).
Mappings []LDAPGroupMapping `json:"mappings,omitempty" gorm:"foreignKey:LDAPConfigID"`
// BindPasswordSet is a read-only flag for the API (whether a service-account
// password is stored) so the secret itself is never exposed. Not persisted;
// populated by the repository on read.
BindPasswordSet bool `json:"bind_password_set" gorm:"-"`
}
LDAPConfig is an LDAP / Active Directory connection for an organization. The table is migrated in every build but is only read/written by the enterprise LDAP authenticator + the admin handler (gated on the sso_ldap entitlement); it stays empty in Community. Mirrors SAMLConfig's per-org, name/display_name shape.
type LDAPGroupMapping ¶
type LDAPGroupMapping struct {
ID uint `json:"id" gorm:"primaryKey"`
LDAPConfigID uint `json:"ldap_config_id" gorm:"index;not null"`
// GroupDN is matched (case-insensitively) against the user's resolved group
// DNs; a bare CN is also accepted for convenience.
GroupDN string `json:"group_dn" gorm:"not null"`
SystemAdmin bool `json:"system_admin" gorm:"not null;default:false"`
// WorkspaceID + WorkspaceRole grant workspace membership (nil = no grant).
WorkspaceID *uint `json:"workspace_id"`
WorkspaceRole WorkspaceRole `json:"workspace_role"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
LDAPGroupMapping maps a directory group to Miabi access. It is reconciled on every login: SystemAdmin grants the platform-admin role, and a WorkspaceID + WorkspaceRole grants membership in that workspace. A mapping with neither is a no-op (kept for documentation).
type LeaseKind ¶
type LeaseKind string
LeaseKind namespaces a lease's RunID so pipeline runs and deploy builds — two different id spaces — never collide in the shared runner_leases table.
type License ¶
type License struct {
ID uint `json:"id" gorm:"primaryKey"`
LicenseID string `json:"license_id" gorm:"uniqueIndex;not null"`
Edition string `json:"edition" gorm:"not null"`
Token string `json:"-" gorm:"type:text;not null"` // never serialized to the API
Customer string `json:"customer"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
GraceDays int `json:"grace_days"`
InstalledBy uint `json:"installed_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
License is the single active commercial license row. The signed Token is the source of truth — it is re-verified against the embedded public key on every load, so the parsed columns are only a cache for display/queries. The table exists in every build (it is migrated unconditionally) but is empty and unused in the Community Edition, which links no license-verification code.
type Metadata ¶
Metadata is a free-form set of key/value labels attached to a resource (Application, Stack, Database, Volume, Route, Secret). It backs provenance, grouping, and the declarative/GitOps features.
Keys under MetadataReservedPrefix are platform-managed ("built-in") and are protected from user modification; all other keys are user-defined and freely editable. Persisted as JSON (gorm serializer).
func DefaultManagedBy ¶
DefaultManagedBy ensures the managed-by built-in is set, defaulting to value when absent, and returns the (possibly newly created) map. Used at creation so every resource records its origin.
func DefaultOwner ¶
DefaultOwner sets the owner reference only when one is not already present, so higher-level callers (marketplace, stacks, apply) that record a richer owner win over a creation-path default. Returns the (possibly updated) map.
func MergeUserMetadata ¶
MergeUserMetadata applies a user-supplied overlay onto the current metadata while protecting built-in keys: reserved keys from current are preserved and cannot be overridden or removed by the overlay; non-reserved keys are replaced wholesale by the overlay (so users can add/remove their own labels). Use on update with the user's desired user-metadata as overlay.
func SanitizeUserMetadata ¶
SanitizeUserMetadata returns a copy of in with all reserved (built-in) keys removed, so user-supplied metadata can never set or spoof platform-managed keys. nil in → nil out.
func SetBuiltin ¶
SetBuiltin sets one or more reserved (built-in) key/value pairs on m, creating the map if needed, and returns it. Reserved keys are authoritative, so this always wins. Pairs are passed as key, value, key, value, …
type MetricSample ¶
type MetricSample struct {
ID uint `json:"id" gorm:"primaryKey"`
ApplicationID uint `json:"application_id" gorm:"index:idx_metric_app_time;not null"`
RecordedAt time.Time `json:"recorded_at" gorm:"index:idx_metric_app_time"`
CPUPercent float64 `json:"cpu_percent"`
MemoryBytes uint64 `json:"memory_bytes"`
MemoryPercent float64 `json:"memory_percent"`
NetRxBytes uint64 `json:"net_rx_bytes"`
NetTxBytes uint64 `json:"net_tx_bytes"`
}
MetricSample is a point-in-time resource sample for an application, captured by the background scraper for short-term history.
type Middleware ¶
type Middleware struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_mw_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace (Goma middleware name).
Name string `json:"name" gorm:"index:idx_mw_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
Type string `json:"type" gorm:"not null"` // Goma middleware kind, e.g. basicAuth, rateLimit
Paths []string `json:"paths,omitempty" gorm:"serializer:json"`
Rule map[string]interface{} `json:"rule,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Middleware is a Goma Gateway middleware owned by a workspace. Routes reference it by Name. Rule holds the type-specific configuration as defined by Goma (mirrors goma-admin's JSON config approach).
type Network ¶
type Network struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_network_workspace_name,unique;not null"`
// Name is the unique slug handle (lowercase [a-z0-9-]) scoped to the workspace.
Name string `json:"name" gorm:"index:idx_network_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
DockerName string `json:"docker_name" gorm:"uniqueIndex;not null"`
Driver string `json:"driver" gorm:"not null;default:bridge"`
Internal bool `json:"internal" gorm:"not null;default:false"`
IsDefault bool `json:"is_default" gorm:"not null;default:false"`
// Imported marks a network that references a pre-existing external Docker
// network by its own name (DockerName = the existing name; nothing recreated).
Imported bool `json:"imported" gorm:"not null;default:false"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Network is a managed Docker network owned by a workspace. The user-facing Name is unique per workspace; DockerName is the platform-managed, globally unique Docker network name (workspace + short id).
type NetworkAllocation ¶
type NetworkAllocation struct {
ID uint `json:"id" gorm:"primaryKey"`
// DockerName is the globally-unique Docker network name this subnet is pinned to.
DockerName string `json:"docker_name" gorm:"uniqueIndex;not null"`
// Subnet is the allocated CIDR (e.g. 10.42.3.0/24); Gateway is its first host.
Subnet string `json:"subnet" gorm:"uniqueIndex;not null"`
Gateway string `json:"gateway" gorm:"not null"`
// NodeID is the node the network lives on (0 = local control-plane node). The
// same subnet may recur across nodes for the same logical network — bridges are
// node-local — so uniqueness is on DockerName/Subnet, not (Subnet, NodeID).
NodeID uint `json:"node_id" gorm:"not null;default:0"`
// Kind labels the allocation (see NetAllocKind*).
Kind string `json:"kind" gorm:"not null;default:workspace"`
CreatedAt time.Time `json:"created_at"`
}
NetworkAllocation is the ledger entry pinning one Docker network to a subnet carved from the platform network pool (MIABI_NETWORK_POOL_CIDR). It is the single source of truth the allocator consults to hand out non-overlapping subnets, so Miabi never depends on Docker's small built-in address pools.
DockerName is unique — the ledger is keyed by the Docker network name, which covers ownerless networks (gateway/overlay) uniformly and lets syncNetworks reuse the same subnet when it recreates a network on another node. Reserved rows (Kind "reserved") use a sentinel DockerName ("reserved:<subnet>").
type NodePlacement ¶
type NodePlacement struct {
// Name is the node's Miabi display name, or a short swarm node id when the node
// is not (yet) correlated to a Miabi server record.
Name string `json:"name"`
// Tasks is the number of the app's running tasks on this node.
Tasks int `json:"tasks"`
}
NodePlacement is where one slice of a cluster app's replicas runs: a node's display name and how many of the app's running tasks the scheduler placed on it. Transient — built on read from live Swarm task placement.
type Notification ¶ added in v1.6.0
type Notification struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index:idx_notif_user_alert,priority:1;not null"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
AlertID *uint `json:"alert_id,omitempty" gorm:"index:idx_notif_user_alert,priority:2"`
Kind string `json:"kind" gorm:"not null;default:alert"` // alert | info
Category AlertCategory `json:"category"`
Severity AlertSeverity `json:"severity" gorm:"not null;default:info"`
Title string `json:"title"`
Body string `json:"body"`
SubjectLink string `json:"subject_link,omitempty"`
// ReadAt is nil while unread. Read state is per-user.
ReadAt *time.Time `json:"read_at,omitempty"`
CreatedAt time.Time `json:"created_at" gorm:"index"`
UpdatedAt time.Time `json:"updated_at"`
}
Notification is a per-user inbox item — the delivery of an Alert (or a standalone info message) to one member. Alerts are workspace-level and shared; notifications are per-user so read/unread stays clean. It renders the alert at delivery time (Title/Body/link), so the inbox is self-contained even after the alert archives. When an alert updates (crash-loop count bump, auto-resolve), the existing notification is updated in place (unique per user+alert) rather than a new row spamming the bell.
type NotificationChannel ¶
type NotificationChannel struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
Type NotificationChannelType `json:"type" gorm:"not null"`
Name string `json:"name"`
Config map[string]string `json:"config" gorm:"serializer:json"`
Events []string `json:"events" gorm:"serializer:json"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
NotificationChannel is a workspace-configured outbound channel that pushes a human-readable message when a subscribed event fires. Transport-specific settings live in Config; secret values (e.g. bot_token) are encrypted at rest and masked in API responses.
type NotificationChannelType ¶
type NotificationChannelType string
NotificationChannelType identifies a notification transport. Telegram ships first; the model is generic so Slack/Discord/email can follow.
const ( ChannelTelegram NotificationChannelType = "telegram" ChannelSlack NotificationChannelType = "slack" ChannelDiscord NotificationChannelType = "discord" )
type OAuthProvider ¶
type OAuthProvider struct {
ID uint `json:"id" gorm:"primaryKey"`
// Name is the globally unique handle used in callback routes.
Name string `json:"name" gorm:"uniqueIndex;not null"`
// DisplayName is the free-text label shown on login buttons.
DisplayName string `json:"display_name"`
Type OAuthProviderType `json:"type" gorm:"not null"`
ClientID string `json:"-" gorm:"not null"`
ClientSecretEnc string `json:"-" gorm:"column:client_secret;not null"`
// OIDC endpoints. For "google" these are derived from the well-known config.
Issuer string `json:"issuer"`
AuthURL string `json:"auth_url"`
TokenURL string `json:"token_url"`
UserInfoURL string `json:"userinfo_url"`
Scopes string `json:"scopes" gorm:"default:'openid email profile'"`
Enabled bool `json:"enabled" gorm:"default:true;not null"`
Hidden bool `json:"hidden" gorm:"default:false;not null"` // hide from login buttons
AutoRegister bool `json:"auto_register" gorm:"default:true;not null"` // auto-create users on first login
// AllowedDomains is a CSV of email domains permitted to sign in / register
// through this provider. Empty means any domain is allowed.
AllowedDomains string `json:"allowed_domains"`
// OrganizationID is the realm this provider belongs to (nullable → default org).
OrganizationID *uint `json:"organization_id" gorm:"index"`
// EmailClaim / NameClaim map a generic OIDC userinfo claim onto the user's
// email / display name. Empty falls back to the standard "email" / "name".
EmailClaim string `json:"email_claim"`
NameClaim string `json:"name_claim"`
// DefaultWorkspaceID + DefaultRole auto-join a newly registered SSO user to a
// workspace with a role on first login. Nil = no auto-join (user is invited
// separately).
DefaultWorkspaceID *uint `json:"default_workspace_id" gorm:"index"`
DefaultRole WorkspaceRole `json:"default_role"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
OAuthProvider is a platform-level SSO identity provider. Secrets are stored encrypted at rest (see internal/services/crypto) and never serialized.
type OAuthProviderType ¶
type OAuthProviderType string
OAuthProviderType is the kind of identity provider.
const ( OAuthProviderGoogle OAuthProviderType = "google" // Google OAuth 2.0 (well-known discovery) OAuthProviderOIDC OAuthProviderType = "oidc" // generic OpenID Connect )
type Organization ¶
type Organization struct {
ID uint `json:"id" gorm:"primaryKey"`
// Name is the globally unique handle (e.g. "default").
Name string `json:"name" gorm:"uniqueIndex;not null"`
// DisplayName is the free-text label shown in the UI.
DisplayName string `json:"display_name"`
// IsDefault marks the org new workspaces and providers attach to. Exactly one
// row is the default.
IsDefault bool `json:"is_default" gorm:"default:false;not null"`
// EnforceSSO disables local password login for this org's users (Enterprise;
// writing it is gated on the sso_saml entitlement). Off by default.
EnforceSSO bool `json:"enforce_sso" gorm:"default:false;not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Organization is a realm that owns identity configuration — SSO providers, SAML, enforced login, and SCIM provisioning. Miabi seeds a single default organization spanning all workspaces. The table and the nullable foreign keys exist from day one so multi-org needs no destructive migration (the same discipline as Server / workspace_id).
type OwnerRef ¶
type OwnerRef struct {
Kind string `json:"kind"` // one of OwnerKind*
ID uint `json:"id,omitempty"` // owning resource id (0 = none/not applicable)
Name string `json:"name,omitempty"` // display name
}
OwnerRef is the parsed owner reference attached to a resource's metadata.
type PasswordResetToken ¶
type PasswordResetToken struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index;not null"`
TokenHash string `json:"-" gorm:"uniqueIndex;not null"`
ExpiresAt time.Time `json:"expires_at" gorm:"not null"`
UsedAt *time.Time `json:"used_at"`
CreatedAt time.Time `json:"created_at"`
}
PasswordResetToken is a single-use, expiring password-reset credential.
type Permission ¶
type Permission string
Permission is a fine-grained capability of the form "resource:action". Each built-in WorkspaceRole maps to a permission set that reproduces the rank-based RequireRole behavior exactly (monotonicity is enforced by permission_test.go). RequirePermission gates routes on a specific capability; custom roles are just alternative permission sets.
const ( PermAppRead Permission = "app:read" PermAppWrite Permission = "app:write" // create / update config / env / attach PermAppDeploy Permission = "app:deploy" // deploy / start / stop / restart / rollback / canary PermAppDelete Permission = "app:delete" PermAppAdmin Permission = "app:admin" // reveal secrets, privileged host mounts PermDatabaseRead Permission = "database:read" PermDatabaseWrite Permission = "database:write" PermDatabaseDelete Permission = "database:delete" PermVolumeRead Permission = "volume:read" PermVolumeWrite Permission = "volume:write" PermDomainRead Permission = "domain:read" PermDomainWrite Permission = "domain:write" PermRouteRead Permission = "route:read" PermRouteWrite Permission = "route:write" PermNetworkRead Permission = "network:read" PermNetworkWrite Permission = "network:write" PermBackupRead Permission = "backup:read" PermBackupRun Permission = "backup:run" PermBackupRestore Permission = "backup:restore" PermSecretRead Permission = "secret:read" PermSecretWrite Permission = "secret:write" PermMemberRead Permission = "member:read" PermMemberWrite Permission = "member:write" PermWorkspaceWrite Permission = "workspace:write" PermWorkspaceDelete Permission = "workspace:delete" )
func AllPermissions ¶
func AllPermissions() []Permission
AllPermissions returns the full catalog in a stable, grouped order (for the UI and the permission-picker in custom roles).
func (Permission) Action ¶
func (p Permission) Action() string
Action returns the action segment ("deploy" for "app:deploy").
func (Permission) Resource ¶
func (p Permission) Resource() string
Resource returns the resource segment of a permission ("app" for "app:deploy").
type PipelineDefinition ¶
type PipelineDefinition struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_pipeline_ws_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_pipeline_ws_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
// ApplicationID optionally binds the pipeline's deploy step to an app.
ApplicationID *uint `json:"application_id,omitempty" gorm:"index"`
// Spec is the raw kind: Pipeline YAML (on/steps). Parsed at run time.
Spec string `json:"spec" gorm:"type:text;not null"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
// WebhookSecret authenticates inbound provider push webhooks that fire this
// pipeline. Generated on create; never serialized.
WebhookSecret string `json:"-" gorm:"not null;default:''"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// LastRun is a non-persisted summary of the pipeline's most recent run,
// populated by the list endpoint for an at-a-glance health signal. nil when
// the pipeline has never run.
LastRun *PipelineRunSummary `json:"last_run,omitempty" gorm:"-"`
}
PipelineDefinition is a versioned, pipeline-as-code definition (kind: Pipeline). The raw YAML spec is stored so the runner reads the same document the repo carries at .miabi/pipeline.yaml.
type PipelineRun ¶
type PipelineRun struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
PipelineID uint `json:"pipeline_id" gorm:"index;not null"`
// Number is the per-pipeline monotonic run counter (#1, #2, …).
Number int `json:"number" gorm:"not null"`
Status PipelineRunStatus `json:"status" gorm:"not null;default:pending"`
// Trigger records how the run started: push | manual | schedule | upstream.
Trigger string `json:"trigger"`
Commit string `json:"commit,omitempty"`
CommitMessage string `json:"commit_message,omitempty" gorm:"type:text"`
// TriggeredByKeyID / TriggeredByUserID attribute the run (mirrors Deployment).
TriggeredByKeyID *uint `json:"triggered_by_key_id,omitempty"`
TriggeredByUserID *uint `json:"triggered_by_user_id,omitempty"`
// ImageID is the artifact the run produced (nil until the build step pushes).
ImageID *uint `json:"image_id,omitempty"`
// RunnerID records which runner executed this run (provenance / "built on
// runner X"); nil when it ran on the internal runner.
RunnerID *uint `json:"runner_id,omitempty"`
Error string `json:"error,omitempty" gorm:"type:text"`
StartedAt *time.Time `json:"started_at,omitempty"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
Steps []PipelineStepRun `json:"steps,omitempty" gorm:"foreignKey:PipelineRunID"`
}
PipelineRun is one execution of a PipelineDefinition: build → test → … → deploy, turning a commit into an image and a Release.
func (PipelineRun) Summary ¶
func (r PipelineRun) Summary() *PipelineRunSummary
Summary projects a run onto its list-view summary.
type PipelineRunStatus ¶
type PipelineRunStatus string
PipelineRunStatus is the lifecycle state of a run or one of its steps.
const ( PipelineRunPending PipelineRunStatus = "pending" PipelineRunRunning PipelineRunStatus = "running" PipelineRunSucceeded PipelineRunStatus = "succeeded" PipelineRunFailed PipelineRunStatus = "failed" PipelineRunCanceled PipelineRunStatus = "canceled" )
func (PipelineRunStatus) IsTerminal ¶
func (s PipelineRunStatus) IsTerminal() bool
IsTerminal reports whether the status is final.
type PipelineRunSummary ¶
type PipelineRunSummary struct {
ID uint `json:"id"`
Number int `json:"number"`
Status PipelineRunStatus `json:"status"`
Trigger string `json:"trigger,omitempty"`
Commit string `json:"commit,omitempty"`
StartedAt *time.Time `json:"started_at,omitempty"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
PipelineRunSummary is a lightweight view of a run for list contexts — enough to render a status badge and "ran 2h ago" without shipping steps or logs.
type PipelineStepRun ¶
type PipelineStepRun struct {
ID uint `json:"id" gorm:"primaryKey"`
PipelineRunID uint `json:"pipeline_run_id" gorm:"index;not null"`
Ordinal int `json:"ordinal" gorm:"not null"`
Name string `json:"name" gorm:"not null"`
Status PipelineRunStatus `json:"status" gorm:"not null;default:pending"`
Image string `json:"image,omitempty"`
Uses string `json:"uses,omitempty"` // built-in step kind (e.g. "deploy")
// Run is the shell command for a container (Image) step. The runner executes
// it in a non-login shell inside the step image — like a GitHub Actions
// `run:` step — so shell features (pipes, &&, env expansion) work. Empty for
// `uses:` built-in steps.
Run string `json:"run,omitempty" gorm:"type:text"`
// ContinueOnError lets the run keep going (and still succeed) when this step
// fails; the step is still recorded failed.
ContinueOnError bool `json:"continue_on_error,omitempty"`
ExitCode int `json:"exit_code"`
// Logs is a bounded tail of the step's output for instant display; the full
// log lives in the log store at LogRef once the step is terminal (see
// plans/log-storage.md). LogRef is empty when the store is disabled or the
// row predates externalization — readers fall back to this tail.
Logs string `json:"logs,omitempty" gorm:"type:text"`
LogRef string `json:"log_ref,omitempty"`
LogBytes int64 `json:"log_bytes,omitempty"`
LogLines int `json:"log_lines,omitempty"`
LogTruncated bool `json:"log_truncated,omitempty"`
StartedAt *time.Time `json:"started_at,omitempty"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
PipelineStepRun is one step within a run, executed in an isolated container.
type Plan ¶
type Plan struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"uniqueIndex;not null"`
Description string `json:"description"`
IsDefault bool `json:"is_default" gorm:"not null;default:false"` // applied to workspaces with no plan
IsActive bool `json:"is_active" gorm:"not null;default:true"`
// Numeric limits (per workspace unless noted). -1 = unlimited, 0 = none.
// The DB default is 0 (= the Go zero value) so GORM's zero-value omission on
// insert is a no-op; an intentional 0 ("none") persists correctly, and -1
// ("unlimited") is non-zero so it is always written.
MaxApps int `json:"max_apps" gorm:"not null;default:0"`
MaxDatabaseInstances int `json:"max_database_instances" gorm:"not null;default:0"`
MaxCronJobs int `json:"max_cron_jobs" gorm:"not null;default:0"`
MaxVolumes int `json:"max_volumes" gorm:"not null;default:0"`
MaxNetworks int `json:"max_networks" gorm:"not null;default:0"`
MaxAPIKeys int `json:"max_api_keys" gorm:"not null;default:0"`
// MaxMembers caps the workspace's member count (owner + invited members).
MaxMembers int `json:"max_members" gorm:"not null;default:0"`
// MaxDatabasesPerInstance caps logical databases within a single instance.
MaxDatabasesPerInstance int `json:"max_databases_per_instance" gorm:"not null;default:0"`
// MaxCPUCores / MaxMemoryMB cap the workspace's aggregate app compute.
MaxCPUCores int `json:"max_cpu_cores" gorm:"not null;default:0"`
MaxMemoryMB int `json:"max_memory_mb" gorm:"not null;default:0"`
// MaxDatabaseInstanceSizeMB caps a single DB instance's declared data-volume
// size; MaxStorageMB caps the workspace's aggregate declared storage
// (volumes + DB instance data volumes).
MaxDatabaseInstanceSizeMB int `json:"max_database_instance_size_mb" gorm:"not null;default:0"`
MaxStorageMB int `json:"max_storage_mb" gorm:"not null;default:0"`
// MaxRunners caps how many build/pipeline runners a workspace may register
// (its own build machines). -1 = unlimited, 0 = none.
MaxRunners int `json:"max_runners" gorm:"not null;default:0"`
// MaxGPUs caps the aggregate number of whole GPU units a workspace's *running*
// apps may hold at once (summed across apps, like MaxCPUCores). 0 = none,
// -1 = unlimited. A stopped app frees its units.
MaxGPUs int `json:"max_gpus" gorm:"not null;default:0"`
// Capabilities (feature gates). Default false for the same omission reason.
AllowCustomTLS bool `json:"allow_custom_tls" gorm:"not null;default:false"`
AllowPrivilegedHostMounts bool `json:"allow_privileged_host_mounts" gorm:"not null;default:false"`
// AllowShellExec gates opening an interactive shell (docker exec) into a
// running application container from the panel.
AllowShellExec bool `json:"allow_shell_exec" gorm:"not null;default:false"`
// that replicas can mount read-write across nodes. Node-local volumes are
// always allowed; this only governs the rwx backends.
AllowSharedStorage bool `json:"allow_shared_storage" gorm:"not null;default:false"`
// AllowDNSProviders gates connecting a managed DNS provider (Cloudflare/Route
// 53/DigitalOcean) for automated ownership verification + app records. Manual
// (copy-paste) DNS always works; this only governs the automation.
AllowDNSProviders bool `json:"allow_dns_providers" gorm:"not null;default:false"`
// AllowCustomLabels gates attaching user-defined Docker labels to app
// containers (for label-driven tools like Traefik). Off by default; the
// reserved-prefix protection (io.miabi.*, com.docker.*) applies regardless.
AllowCustomLabels bool `json:"allow_custom_labels" gorm:"not null;default:false"`
// AllowPlatformRunners grants this workspace's build/pipeline jobs access to
// the platform-shared runner pool (in addition to any runners it owns). Off by
// default; owned runners are always usable.
AllowPlatformRunners bool `json:"allow_platform_runners" gorm:"not null;default:false"`
// AllowCustomBuilder gates a per-app custom buildpack builder image. A custom
// builder runs on the runner with docker-daemon access, so it is a privileged
// input on shared/multi-tenant runners; off by default (the platform default
// builder is used instead).
AllowCustomBuilder bool `json:"allow_custom_builder" gorm:"not null;default:false"`
// SecurityProfile hardens how this workspace's application and job containers
// run. "" / "default" = the image's default user (unchanged); "restricted"
// forces a non-root platform UID. Default "default" is the zero-equivalent, so
// the GORM zero-value omission still round-trips an intentional value.
SecurityProfile string `json:"security_profile" gorm:"not null;default:'default'"`
// AllowOfficialImageUser lets applications installed from an official
// marketplace template keep the image's own default user even when this
// workspace's SecurityProfile is "restricted". Only official-source installs
// qualify (a tenant cannot self-declare an app official), and it never relaxes
// a platform-wide MIABI_FORCE_NON_ROOT_USER mandate. Off by default.
AllowOfficialImageUser bool `json:"allow_official_image_user" gorm:"not null;default:false"`
// AllowGPU gates whether this workspace's apps may request GPU devices at all.
// Off by default, like AllowSharedStorage: a workspace cannot request any GPU
// until its plan opts in. Attaching a GPU is device passthrough (privileged),
// so it is gated hard here and is incompatible with the restricted profile.
AllowGPU bool `json:"allow_gpu" gorm:"not null;default:false"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Plan is an admin-defined per-workspace quota + capability template. Numeric limits use -1 for unlimited and 0 for none; capability booleans gate features.
type PlatformBackup ¶
type PlatformBackup struct {
ID uint `json:"id" gorm:"primaryKey"`
Subject PlatformBackupSubject `json:"subject" gorm:"not null"` // database | volume
VolumeName string `json:"volume_name,omitempty"` // target volume (subject=volume)
Status BackupStatus `json:"status" gorm:"not null;default:pending"`
Trigger string `json:"trigger"` // manual | scheduled
Destination string `json:"destination"` // local | s3
S3Bucket string `json:"s3_bucket,omitempty"`
S3Path string `json:"s3_path,omitempty"` // remote folder prefix used
Filename string `json:"filename,omitempty"` // artifact object name
SizeBytes int64 `json:"size_bytes"`
// Logs is a bounded tail of the backup output for instant display; the full
// log lives in the log store at LogRef once the run is terminal (see
// plans/log-storage.md). LogRef is empty when the store is disabled or the
// row predates externalization — readers fall back to this tail.
Logs string `json:"logs,omitempty" gorm:"type:text"`
LogRef string `json:"log_ref,omitempty"`
LogBytes int64 `json:"log_bytes,omitempty"`
LogLines int `json:"log_lines,omitempty"`
LogTruncated bool `json:"log_truncated,omitempty"`
Error string `json:"error,omitempty" gorm:"type:text"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `json:"created_at"`
}
PlatformBackup is a single disaster-recovery backup run of the platform itself — the control-plane database or a platform volume. It is distinct from the per-workspace Backup/VolumeBackup tables (tenant data) and reuses BackupStatus and the existing one-shot/logging lifecycle.
type PlatformBackupSettings ¶
type PlatformBackupSettings struct {
ID uint `json:"id" gorm:"primaryKey"`
// Destination. S3 is strongly recommended for DR (a local artifact on the box
// you are recovering from is no DR at all).
S3Enabled bool `json:"s3_enabled"`
S3Endpoint string `json:"s3_endpoint,omitempty"`
S3Bucket string `json:"s3_bucket,omitempty"`
S3Region string `json:"s3_region,omitempty"`
S3AccessKey string `json:"s3_access_key,omitempty"`
S3SecretKeyEnc string `json:"-" gorm:"column:s3_secret_key_enc"` // crypto.Encrypt (platform scope)
S3UseSSL bool `json:"s3_use_ssl"`
S3ForcePathStyle bool `json:"s3_force_path_style"`
// Path prefixes within the bucket.
DatabaseBackupPath string `json:"database_backup_path,omitempty"`
VolumeBackupPath string `json:"volume_backup_path,omitempty"`
// Schedule + retention.
ScheduleEnabled bool `json:"schedule_enabled"`
ScheduleCron string `json:"schedule_cron,omitempty"`
MaxBackups int `json:"max_backups"`
RetentionDays int `json:"retention_days"`
Volumes []string `json:"volumes" gorm:"serializer:json"` // platform volumes to include
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// S3SecretSet reports whether a secret key is stored, without exposing it.
// Not persisted; populated on read so the UI can render "••••• (set)".
S3SecretSet bool `json:"s3_secret_set" gorm:"-"`
}
PlatformBackupSettings is the single-row platform backup target + policy: the S3 destination (mirroring WorkspaceBackupSettings), the schedule, retention, and the set of platform volumes to include. The S3 secret is encrypted at rest with the platform-scoped crypto.Encrypt and never returned.
type PlatformBackupSubject ¶
type PlatformBackupSubject string
PlatformBackupSubject identifies what a platform backup run captured: Miabi's own control-plane database, or one of the platform/system Docker volumes.
const ( PlatformBackupDatabase PlatformBackupSubject = "database" PlatformBackupVolume PlatformBackupSubject = "volume" )
type PortBinding ¶
type PortBinding struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
ApplicationID uint `json:"application_id" gorm:"index;not null"`
ContainerPort int `json:"container_port" gorm:"not null"`
Protocol string `json:"protocol" gorm:"not null;default:tcp"`
HostPort int `json:"host_port" gorm:"not null"`
Status PortBindingStatus `json:"status" gorm:"not null;default:pending;index"`
// ServerID is the node the host port is published on. Host ports are a
// per-host resource, so conflict checks + allocation are scoped to it
// (0 = the local/manager node). Backfilled from the owning app.
ServerID uint `json:"server_id" gorm:"index;not null;default:0"`
// Managed marks a control-plane auto-forward binding (created for a
// port-forward node's route ingress) rather than a user request. Managed
// bindings are auto-approved and never enter the admin review queue.
Managed bool `json:"managed" gorm:"not null;default:false"`
// BindIP is the host interface the port is published on ("" = all/0.0.0.0).
// Managed bindings use the node's private address so ingress stays private.
BindIP string `json:"bind_ip,omitempty"`
RequestedBy uint `json:"requested_by"`
ReviewedBy *uint `json:"reviewed_by,omitempty"`
ReviewNote string `json:"review_note,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
PortBinding is a request to publish a container port on a host port. Because host ports are a node-wide shared resource, a binding is only published once a platform admin approves it.
type PortBindingStatus ¶
type PortBindingStatus string
PortBindingStatus is the review state of a host port binding request.
const ( PortBindingPending PortBindingStatus = "pending" PortBindingApproved PortBindingStatus = "approved" PortBindingRejected PortBindingStatus = "rejected" )
type Registry ¶
type Registry struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_registry_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_registry_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
// Server is the registry host, e.g. "registry-1.docker.io", "ghcr.io",
// "registry.gitlab.com". Empty defaults to Docker Hub.
Server string `json:"server"`
Username string `json:"username"`
// Secret holds the password or access token, encrypted at rest.
Secret string `json:"-" gorm:"not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// HasSecret is a transient flag for responses (never persisted).
HasSecret bool `json:"has_secret" gorm:"-"`
}
Registry is a stored container-registry credential used to pull private images at deploy time. The Secret (password / access token) is encrypted at rest and never serialized.
type RegistrySettings ¶
type RegistrySettings struct {
ID uint `json:"id" gorm:"primaryKey"`
// Enabled runs the registry container and seeds its gateway route/middleware.
// Default false → a no-op so single-node installs are unchanged.
Enabled bool `json:"enabled"`
// Host is the public registry hostname (e.g. registry.<external-base-domain>),
// the docker login target. Derived from the external base domain when blank.
Host string `json:"host,omitempty"`
// StorageType is "filesystem" (a managed volume) or "s3" (S3/MinIO).
StorageType string `json:"storage_type" gorm:"not null;default:filesystem"`
// Filesystem driver: the managed data volume.
VolumeName string `json:"volume_name,omitempty"`
// S3 / MinIO driver.
S3Endpoint string `json:"s3_endpoint,omitempty"`
S3Bucket string `json:"s3_bucket,omitempty"`
S3Region string `json:"s3_region,omitempty"`
S3AccessKey string `json:"s3_access_key,omitempty"`
S3SecretKeyEnc string `json:"-" gorm:"column:s3_secret_key_enc"` // crypto.Encrypt (platform scope)
S3ForcePathStyle bool `json:"s3_force_path_style"`
// DeleteEnabled turns on the registry's delete API (a prerequisite for GC).
DeleteEnabled bool `json:"delete_enabled"`
// PerWorkspaceQuotaMB caps a workspace namespace's total blob size (0 = none).
PerWorkspaceQuotaMB int `json:"per_workspace_quota_mb"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// S3SecretSet reports whether a secret key is stored, without exposing it.
// Not persisted; populated on read so the UI can render "••••• (set)".
S3SecretSet bool `json:"s3_secret_set" gorm:"-"`
}
RegistrySettings is the single-row, platform-scoped configuration for the built-in Docker registry (CNCF distribution / `registry:3`). One registry per platform, so this mirrors PlatformBackupSettings (global, not per-workspace). The S3 secret is encrypted at rest with the platform-scoped crypto.Encrypt and never returned by the API.
func (*RegistrySettings) UsesS3 ¶
func (r *RegistrySettings) UsesS3() bool
UsesS3 reports whether the registry is configured for the S3 storage driver.
type Release ¶
type Release struct {
ID uint `json:"id" gorm:"primaryKey"`
// (ApplicationID, Version) is unique: concurrent deploys that both compute the
// same MAX(version)+1 can no longer both persist — one hits the constraint and
// rolls back instead of corrupting history with duplicate versions.
ApplicationID uint `json:"application_id" gorm:"index:idx_release_app_version,unique;index;not null"`
DeploymentID uint `json:"deployment_id" gorm:"not null"`
Version int `json:"version" gorm:"index:idx_release_app_version,unique;not null"`
Image string `json:"image" gorm:"not null"`
ContainerID string `json:"container_id"`
Active bool `json:"active" gorm:"not null;default:false"`
// Adopted marks a release whose ContainerID points at a pre-existing container
// adopted by the import flow (not created by the deploy pipeline). It lets the
// Docker browser badge the container "adopted" and is cleared when a native
// deploy supersedes it.
Adopted bool `json:"adopted" gorm:"not null;default:false"`
// Pinned releases are protected from deletion during cleanup.
Pinned bool `json:"pinned" gorm:"not null;default:false"`
// Provenance (GitOps & CI/CD). A release generalizes into a promotable
// artifact: image digest + config snapshot + version + provenance, flowing
// through environments. These fields are populated when a deploy originates
// from a pipeline run or a digest-pinned apply; they stay zero for legacy
// hand-triggered deploys.
Digest string `json:"digest,omitempty"` // sha256:… the release ran
Commit string `json:"commit,omitempty"` // source commit, when known
PipelineRunID *uint `json:"pipeline_run_id,omitempty" gorm:"index"` // producing run
ImageID *uint `json:"image_id,omitempty" gorm:"index"` // catalog artifact
EnvironmentID *uint `json:"environment_id,omitempty" gorm:"index"` // promotion stage
CreatedAt time.Time `json:"created_at"`
}
Release is a successfully deployed, rollback-able version of an application.
type ReleaseApproval ¶
type ReleaseApproval struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
ReleaseID uint `json:"release_id" gorm:"index;not null"`
EnvironmentID *uint `json:"environment_id,omitempty" gorm:"index"`
ApproverID uint `json:"approver_id" gorm:"not null"`
Approved bool `json:"approved" gorm:"not null;default:true"`
Comment string `json:"comment,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
ReleaseApproval records one approver's decision on promoting a release into an environment. A promotion needs Environment.RequiredApprovals approvals.
type ResourcePolicy ¶
type ResourcePolicy struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_resource_policy;not null"`
UserID uint `json:"user_id" gorm:"index:idx_resource_policy;not null"`
ResourceType string `json:"resource_type" gorm:"index:idx_resource_policy;not null"`
ResourceID uint `json:"resource_id" gorm:"index:idx_resource_policy;not null"`
Permissions []string `json:"permissions" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
ResourcePolicy grants a user a set of permissions on a single resource (Enterprise; gated on resource_policies for writes). It augments the workspace-level role: a member may be a viewer everywhere yet hold app:deploy on one specific app. The table is empty in Community.
func (*ResourcePolicy) Grants ¶
func (p *ResourcePolicy) Grants(perm Permission) bool
Grants reports whether the policy includes a permission.
type RestartPolicy ¶
type RestartPolicy string
RestartPolicy is the Docker restart policy applied to an app's container. It mirrors the engine's policies; the platform default is "unless-stopped".
const ( RestartNo RestartPolicy = "no" // never restart RestartAlways RestartPolicy = "always" // always restart RestartUnlessStopped RestartPolicy = "unless-stopped" // restart unless explicitly stopped (default) RestartOnFailure RestartPolicy = "on-failure" // restart only on non-zero exit )
type Route ¶
type Route struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_route_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace (Goma route name).
Name string `json:"name" gorm:"index:idx_route_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
ApplicationID uint `json:"application_id" gorm:"index;not null"`
Path string `json:"path" gorm:"not null;default:/"`
Hosts []string `json:"hosts,omitempty" gorm:"serializer:json"`
Methods []string `json:"methods,omitempty" gorm:"serializer:json"`
Middlewares []string `json:"middlewares,omitempty" gorm:"serializer:json"` // middleware names
Rewrite string `json:"rewrite,omitempty"` // path rewrite (Goma route rewrite)
TargetPort int `json:"target_port"` // 0 = use the app's port
TLSMode RouteTLSMode `json:"tls_mode" gorm:"not null;default:acme"`
// TLSProvider names the Goma certManager provider that issues/serves this
// route's cert (the multi-provider certManager). Empty = the gateway default.
TLSProvider string `json:"tls_provider,omitempty"`
// Generated marks a platform-generated external-access route (managed from the
// app's External access card, shown read-only in the Routes UI).
Generated bool `json:"generated" gorm:"not null;default:false"`
// AdvancedConfig is a raw Goma route YAML the admin authors directly; when
// set, it supersedes the structured fields at render time (Miabi still
// injects name + backends + tls so the route can't misroute). Empty = simple.
AdvancedConfig string `json:"advanced_config,omitempty" gorm:"type:text"`
// CertificateID references a stored Certificate when TLSMode=custom.
CertificateID *uint `json:"certificate_id,omitempty" gorm:"index"`
// Certificate declares the FK so deleting the referenced certificate nulls
// this pointer (ON DELETE SET NULL) instead of leaving it dangling. The route
// falls back to its default TLS mode. Association is not serialized.
Certificate *Certificate `json:"-" gorm:"foreignKey:CertificateID;constraint:OnDelete:SET NULL"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
// Status is the route's config-sync status, set whenever the workspace proxy
// config is reconciled.
Status RouteStatus `json:"status" gorm:"not null;default:pending"`
// StatusReason explains a non-live status (e.g. "domain not verified",
// "disabled", or the gateway write error). Empty when live.
StatusReason string `json:"status_reason,omitempty"`
// SyncedAt is when the route was last reconciled into the gateway config.
SyncedAt *time.Time `json:"synced_at,omitempty"`
// Metadata holds free-form labels; "miabi.io/" keys are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// HasCustomCert is a transient flag for responses (never persisted).
HasCustomCert bool `json:"has_custom_cert" gorm:"-"`
// DNSTarget / DNSHostname are the public address an A/AAAA (or CNAME) record
// for this route's hosts should point to — the gateway that terminates the
// route. Transient, populated on read.
DNSTarget string `json:"dns_target,omitempty" gorm:"-"`
DNSHostname string `json:"dns_hostname,omitempty" gorm:"-"`
// Backends are the actual upstream endpoints the gateway uses for this route
// (the node-local DNS alias, or a port-forward node's address:hostPort).
// Transient, populated on read so the UI shows the real backend.
Backends []string `json:"backends,omitempty" gorm:"-"`
}
Route is a Goma Gateway route owned by a workspace and bound to an application. The container backend is injected by the platform at render time (the app's network alias), so users never set it.
type RouteStatus ¶
type RouteStatus string
RouteStatus is a route's config-sync status with the gateway — whether Goma is actually serving the route's config. It is NOT upstream health: RouteLive means "Goma is configured to serve this route", not "the backend answers 200".
const ( // RouteStatusPending is the initial state before the route has been synced. RouteStatusPending RouteStatus = "pending" // RouteStatusLive means the route was synced and Goma is serving it (enabled, // and every host falls under a verified domain). RouteStatusLive RouteStatus = "live" // RouteStatusOffline means the route is synced but not served — it is disabled, // or one of its hosts is not under a verified domain (rendered enabled:false). RouteStatusOffline RouteStatus = "offline" // RouteStatusError means the last attempt to push the route's config to the // gateway failed; StatusReason carries the detail. RouteStatusError RouteStatus = "error" )
type RouteTLSMode ¶
type RouteTLSMode string
RouteTLSMode selects how TLS is served for a route's hosts.
const ( RouteTLSNone RouteTLSMode = "none" RouteTLSACME RouteTLSMode = "acme" // Goma's global ACME issuer RouteTLSCustom RouteTLSMode = "custom" // user-supplied certificate )
type Runner ¶
type Runner struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
// Name is a URL-safe slug handle, unique within its owner (per workspace for
// owned runners; among shared runners for platform ones).
Name string `json:"name" gorm:"index:idx_runner_ws_name,unique;not null"`
// DisplayName is the free-text label shown in the UI (defaults to Name).
DisplayName string `json:"display_name"`
// WorkspaceID is nil for platform-shared runners, set for workspace-owned
// ones. Part of the (workspace_id, name) uniqueness index.
WorkspaceID *uint `json:"workspace_id,omitempty" gorm:"index:idx_runner_ws_name,unique"`
Scope RunnerScope `json:"scope" gorm:"not null;default:workspace"`
// Labels route jobs to eligible runners (e.g. "arch=amd64", "buildkit",
// "gpu"). A job runs only where its required labels are a subset of these.
Labels []string `json:"labels,omitempty" gorm:"serializer:json"`
// Concurrency is how many jobs the runner may lease at once (declared by the
// operator; the scheduler tracks live leases against it).
Concurrency int `json:"concurrency" gorm:"not null;default:1"`
// Self-reported platform facts (filled on connect).
OS string `json:"os,omitempty"`
Arch string `json:"arch,omitempty"`
Version string `json:"version,omitempty"`
// RemoteIP is the source address of the runner's most recent tunnel
// connection (last known; persists across disconnects for the detail view).
RemoteIP string `json:"remote_ip,omitempty"`
Status RunnerStatus `json:"status" gorm:"not null;default:offline"`
// Cordoned holds the runner out of scheduling without disconnecting it
// (operator pause), mirroring Server.Cordoned.
Cordoned bool `json:"cordoned" gorm:"not null;default:false"`
// Enabled is the admin/owner on-off switch; a disabled runner never receives
// jobs even if connected. Defaults true.
Enabled bool `json:"enabled" gorm:"not null;default:true"`
// TokenHash is the SHA-256 of the one-time registration token; the plaintext
// is shown once at creation and never stored (mirrors Server.TokenHash).
TokenHash string `json:"-" gorm:"index"`
// Ephemeral marks a per-job, autoscaled runner (spun up for one job and torn
// down after). Standing runners are non-ephemeral. Reserved for the later
// autoscaling phase; excluded from the owned-runner quota when set.
Ephemeral bool `json:"ephemeral" gorm:"not null;default:false"`
// Connected reflects a live tunnel (transient; set by the connection manager).
Connected bool `json:"connected" gorm:"-"`
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
// ConnectedSince is when the current unbroken connection began — reset on every
// reconnect, cleared on disconnect. LastSeenAt cannot answer "how long has this
// runner been up?" because the heartbeat refreshes it every 30s; the offline
// alert needs that to avoid clearing on a runner that is flapping.
ConnectedSince *time.Time `json:"connected_since,omitempty"`
CreatedByID *uint `json:"created_by_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Runner is a machine dedicated to build and pipeline execution. Unlike a Server (which runs apps/databases), a runner never hosts workloads: it leases build jobs, executes each step in an isolated container, builds and pushes the image to a registry, and reports status — the app node only ever pulls the resulting digest. A runner dials in over the same outbound tunnel shape as a node agent, but with a distinct, tightly-scoped registration token.
A runner is either workspace-owned (WorkspaceID set, Scope=workspace) or platform-shared (WorkspaceID nil, Scope=shared), following the nullable WorkspaceID modeling used by TemplateSource.
type RunnerLease ¶
type RunnerLease struct {
ID uint `json:"id" gorm:"primaryKey"`
RunnerID uint `json:"runner_id" gorm:"index;not null"`
// Kind namespaces RunID (pipeline run vs deploy build); see LeaseKind.
Kind LeaseKind `json:"kind" gorm:"not null;default:pipeline"`
// RunID is the job this lease claims (a PipelineRun or a Deployment, per Kind).
// A partial unique index on (kind, run_id) for active rows enforces the
// at-most-one active claim per job at the DB level.
RunID uint `json:"run_id" gorm:"index;not null"`
StepID *uint `json:"step_id,omitempty"`
Status RunnerLeaseStatus `json:"status" gorm:"not null;default:active;index"`
LeasedAt time.Time `json:"leased_at"`
// ExpiresAt is the lease deadline; a live runner renews it, a dead one lets it
// lapse (→ requeue). Mirrors the job deadline.
ExpiresAt time.Time `json:"expires_at" gorm:"index"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
RunnerLease is a runner's at-most-once claim on a job — a pipeline run or a deploy build (per Kind) — and, optionally, a specific step. It is created when a runner leases a job and released when the job goes terminal; an active lease past its ExpiresAt is a dead runner, so the sweeper marks it expired and the job requeues. Concurrency is a runner's active-lease count vs its Concurrency.
type RunnerLeaseStatus ¶
type RunnerLeaseStatus string
RunnerLeaseStatus is the state of a runner's claim on a pipeline run.
const ( // LeaseActive: the runner holds the job and is executing it. Exactly one // active lease may exist per run (the at-most-once claim). LeaseActive RunnerLeaseStatus = "active" // LeaseDone: the run reached a terminal state and the lease was released. LeaseDone RunnerLeaseStatus = "done" // LeaseExpired: the lease passed its deadline without completing (a dead // runner); the sweeper requeues the run onto another runner. LeaseExpired RunnerLeaseStatus = "expired" )
type RunnerScope ¶
type RunnerScope string
RunnerScope is who may schedule jobs onto a runner.
const ( // ScopeWorkspace: a runner registered and owned by a single workspace; only // that workspace's jobs run on it (WorkspaceID is set). ScopeWorkspace RunnerScope = "workspace" // workspace whose plan grants the platform-runners capability. Managed by // admins, mirroring a global TemplateSource/OAuthProvider. ScopeShared RunnerScope = "shared" )
type RunnerStatus ¶
type RunnerStatus string
RunnerStatus is the reachability state of a runner, mirroring ServerStatus.
const ( // RunnerStatusOnline: the runner has a live tunnel and is leasing jobs. RunnerStatusOnline RunnerStatus = "online" // RunnerStatusOffline: no live tunnel (never connected, or heartbeat lapsed). RunnerStatusOffline RunnerStatus = "offline" // RunnerStatusDraining: connected but finishing its in-flight jobs before it // disconnects (no new leases). Distinct from Cordoned, which is an operator // hold applied while the runner may still be online. RunnerStatusDraining RunnerStatus = "draining" )
type RuntimeKind ¶
type RuntimeKind string
RuntimeKind is how an application runs: a single Docker container (default, works on plain Docker) or a replicated Swarm service (cluster mode). It is auto-gated on the manager being a swarm manager — a "service" app only deploys when cluster mode is enabled.
const ( // RuntimeContainer runs the app as one plain Docker container on its node. RuntimeContainer RuntimeKind = "container" // RuntimeService runs the app as a replicated Swarm service on the workspace // overlay network. RuntimeService RuntimeKind = "service" )
type SAMLConfig ¶
type SAMLConfig struct {
ID uint `json:"id" gorm:"primaryKey"`
OrganizationID *uint `json:"organization_id" gorm:"index"`
// Name is the globally unique handle.
Name string `json:"name" gorm:"uniqueIndex;not null"`
// DisplayName is the free-text label shown in the UI.
DisplayName string `json:"display_name"`
// IdP metadata: a URL to fetch, or inline XML. One of the two is required.
IDPMetadataURL string `json:"idp_metadata_url"`
IDPMetadataXML string `json:"idp_metadata_xml" gorm:"type:text"`
// SPEntityID overrides the service-provider entity ID (defaults to the SP
// metadata URL when blank).
SPEntityID string `json:"sp_entity_id"`
// Attribute names mapping the assertion onto the user. Blank falls back to
// the standard "email" / "displayName" (and the NameID for email).
AttrEmail string `json:"attr_email"`
AttrName string `json:"attr_name"`
Enabled bool `json:"enabled" gorm:"default:true;not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
SAMLConfig is a SAML 2.0 identity-provider connection for an organization. The table is migrated in every build but is only read/written by the enterprise SAML handler (gated on the sso_saml entitlement); it stays empty in Community.
type SCIMToken ¶
type SCIMToken struct {
ID uint `json:"id" gorm:"primaryKey"`
OrganizationID *uint `json:"organization_id" gorm:"index"`
Name string `json:"name" gorm:"not null"`
TokenHash string `json:"-" gorm:"uniqueIndex;not null"`
LastUsedAt *time.Time `json:"last_used_at"`
CreatedAt time.Time `json:"created_at"`
}
SCIMToken is a bearer credential an identity provider uses to call the SCIM 2.0 provisioning endpoint. Only the SHA-256 hash is stored; the plaintext is shown once at creation. The table is migrated in every build but only read/written by the enterprise SCIM handler (gated on the scim entitlement).
type SIEMConfig ¶
type SIEMConfig struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"not null"`
Sink string `json:"sink" gorm:"not null"` // syslog | webhook | s3
Endpoint string `json:"endpoint"` // syslog addr / webhook URL / s3 bucket+prefix
Format string `json:"format" gorm:"default:'json'"` // json | cef
// AuthHeaderEnc is a webhook Authorization value, encrypted at rest. Never
// serialized.
AuthHeaderEnc string `json:"-" gorm:"column:auth_header"`
Enabled bool `json:"enabled" gorm:"default:true;not null"`
// LastShippedID is the durable cursor: the highest audit-log id confirmed
// shipped to this sink. The streamer only advances it after a successful ship.
LastShippedID uint `json:"last_shipped_id"`
LastError string `json:"last_error"`
LastShippedAt *time.Time `json:"last_shipped_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
SIEMConfig is one external audit-streaming target (Enterprise; gated on the siem_stream entitlement). The streamer ships audit events at-least-once and records its progress in LastShippedID — a durable cursor that survives restarts and sink outages. The table is empty in Community.
type Secret ¶
type Secret struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_secret_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace; env refs use it.
Name string `json:"name" gorm:"index:idx_secret_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
ValueEnc string `json:"-" gorm:"column:value_enc;type:text"` // encrypted
Description string `json:"description"`
// Version bumps on each value change (rotation), for display/rollback.
Version int `json:"version" gorm:"not null;default:1"`
UpdatedByID *uint `json:"updated_by_id,omitempty"`
// Managed marks a secret auto-created and owned by a platform resource (e.g.
// a managed database). Its value is derived (rotate via the owner, not by
// hand) and its lifecycle follows the owner. OwnerKind/OwnerID identify it.
Managed bool `json:"managed" gorm:"not null;default:false"`
OwnerKind string `json:"owner_kind,omitempty" gorm:"index:idx_secret_owner"`
OwnerID uint `json:"owner_id,omitempty" gorm:"index:idx_secret_owner"`
// Metadata holds free-form labels; "miabi.io/" keys are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Secret is a workspace-scoped named secret (the Vault). Env var values reference it by name (`${{ secrets.NAME }}`); the reference is resolved into a container's environment at deploy/job time and never returned by the API. The value is encrypted at rest.
type Server ¶
type Server struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"uniqueIndex;not null"`
// Slug is a stable URL-safe identifier derived from the name, used in the
// Goma HTTP-provider endpoint path (/api/v1/provider/{slug}).
Slug string `json:"slug" gorm:"index"`
Connectivity ServerConnectivity `json:"connectivity" gorm:"not null;default:port-forward"`
// AccessMode is how the control plane reaches this node's Docker engine.
// Existing rows backfill to "agent" (column default); the local node is set
// to "socket" at bootstrap.
AccessMode ServerAccessMode `json:"access_mode" gorm:"not null;default:agent"`
// DockerEndpoint is the Docker host for non-agent modes: unix://… (socket) or
// tcp://host:2376 (api). Unused for agent.
DockerEndpoint string `json:"docker_endpoint"`
IsLocal bool `json:"is_local" gorm:"not null;default:false"`
// Role is the node's cluster role: "manager" for the control-plane node,
// "node" for workers.
Role ServerRole `json:"role" gorm:"not null;default:node"`
Status ServerStatus `json:"status" gorm:"not null;default:unknown"`
LastSeenAt *time.Time `json:"last_seen_at"`
// api (TCP) TLS material — optional (plaintext when empty). PEM-encoded; the
// private key is encrypted at rest and never serialized.
TLSCACert string `json:"-" gorm:"type:text"`
TLSCert string `json:"-" gorm:"type:text"`
TLSKeyEnc string `json:"-" gorm:"column:tls_key_enc;type:text"`
// TLSEnabled is a transient flag for responses (whether TLS material is set).
TLSEnabled bool `json:"tls_enabled" gorm:"-"`
// Remote-node fields.
// Address is the host/IP the reverse proxy uses to reach this node's
// published app ports (e.g. "10.0.0.7").
Address string `json:"address,omitempty"`
// PublicIP / PublicHostname are the node's externally reachable address that
// end users point DNS at (A/AAAA record → PublicIP, or CNAME → PublicHostname).
// Distinct from Address (the private host the proxy dials): an edge-gateway
// node terminates its own ingress here, while the control-plane (local) node's
// value is the default DNS target for every port-forward app.
PublicIP string `json:"public_ip,omitempty"`
PublicHostname string `json:"public_hostname,omitempty"`
// TokenHash is the SHA-256 of the agent join token; the plaintext is shown
// once at creation and never stored.
TokenHash string `json:"-" gorm:"index"`
// GatewayTokenEnc is the node's edge-gateway provider token, encrypted at
// rest (recoverable, unlike the join token). The gateway authenticates its
// HTTP-provider polls with it; minted lazily on first edge deploy so a
// gateway can be (re)deployed on demand without the plaintext join token.
GatewayTokenEnc string `json:"-"`
// GatewayDeployedAt is when the node's gateway was last deployed (display).
GatewayDeployedAt *time.Time `json:"gateway_deployed_at,omitempty"`
// GatewayConfigYAML is the admin's custom goma.yml for this node's edge
// gateway; empty = use the rendered default. The provider token is injected
// as the INSTANCE_API_KEY env var at deploy, so this holds no secret.
GatewayConfigYAML string `json:"-" gorm:"type:text"`
// GatewayImage overrides the edge-gateway image/tag for this node; empty =
// the resolved catalog/default image.
GatewayImage string `json:"gateway_image,omitempty"`
// GatewayContainer is the Docker container name of an *imported* (adopted)
// gateway this node tracks instead of the managed mb-node-gateway. Empty = the
// managed default. Set by the gateway import flow so status/logs resolve to
// the adopted container.
GatewayContainer string `json:"gateway_container,omitempty"`
// GatewayImported marks that the node's gateway was adopted from a pre-existing
// container rather than deployed by Miabi.
GatewayImported bool `json:"gateway_imported" gorm:"not null;default:false"`
// GatewayRedisPasswordEnc is the password (encrypted) for the per-node Redis
// the edge gateway uses for shared cache + distributed rate limiting. Minted on
// first gateway deploy of a remote edge node. Empty on the manager, which
// reuses the platform Redis instead of running its own.
GatewayRedisPasswordEnc string `json:"-"`
// GatewayUpdate is the live state of an in-flight safe gateway update (test →
// promote). Persisted as JSON so progress survives a reconnect and is visible
// to every SSE subscriber; nil when no update is running.
GatewayUpdate *GatewayUpdateProgress `json:"gateway_update,omitempty" gorm:"serializer:json"`
// AgentConnected reflects a live agent tunnel (transient; set by the
// connection manager).
AgentConnected bool `json:"agent_connected" gorm:"-"`
AgentVersion string `json:"agent_version,omitempty"`
Cordoned bool `json:"cordoned" gorm:"not null;default:false"`
Labels map[string]string `json:"labels,omitempty" gorm:"serializer:json"`
// Cluster (Docker Swarm) fields. Clustering is opt-in and auto-detected;
// these stay empty on plain Docker.
//
// SwarmNodeID is persisted: it correlates this Miabi node to its Docker Swarm
// node, set when the node joins the swarm (and for the manager from its own
// `docker info`). It is the stable key the Nodes page uses to look the node up
// in `docker node ls`.
SwarmNodeID string `json:"swarm_node_id,omitempty" gorm:"index"`
// AutoJoined marks a node the CLUSTER brought in rather than an admin: the
// global agent service landed on a swarm member, the agent registered itself,
// and Miabi created this record. It distinguishes "I added this machine" from
// "the swarm gave me this machine", which is the difference between a node an
// operator chose to place workloads on and one that simply exists because it is
// in the swarm.
AutoJoined bool `json:"auto_joined" gorm:"not null;default:false"`
// The rest are transient (not stored): populated from the manager's
// `docker node ls` each time nodes are listed, so the Nodes page can show a
// node's swarm role and availability.
//
// SwarmRole is the node's role in the swarm: leader | manager | worker, or
// "standalone" when cluster mode is on but this node is not a swarm member.
// Blank when cluster mode is off entirely.
SwarmRole string `json:"swarm_role,omitempty" gorm:"-"`
// SwarmAvailability is the scheduling availability: active | pause | drain.
SwarmAvailability string `json:"swarm_availability,omitempty" gorm:"-"`
// SwarmState is the swarm-reported reachability: ready | down | unknown |
// disconnected.
SwarmState string `json:"swarm_state,omitempty" gorm:"-"`
// InSwarm reports whether this node is currently a member of the swarm.
InSwarm bool `json:"in_swarm" gorm:"-"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Server is a Docker host (node). The local node runs alongside the control plane; remote nodes run a `miabi agent` that dials in and exposes their Docker engine over a tunnel.
type ServerAccessMode ¶
type ServerAccessMode string
ServerAccessMode is how the control plane reaches a node's Docker engine. Distinct from ServerConnectivity (how the proxy reaches apps).
const ( // AccessSocket: a local/Unix (or custom) Docker host where the control plane // runs (DockerEndpoint, e.g. unix:///var/run/docker.sock). The control-plane // node uses this. AccessSocket ServerAccessMode = "socket" // AccessAgent: the node's agent dials in over a tunnel (NAT-friendly, no // inbound ports on the node). DockerEndpoint is unused. AccessAgent ServerAccessMode = "agent" // AccessAPI: the control plane dials the node's Docker API over TCP // (DockerEndpoint = tcp://host:2376), optionally with TLS/mTLS. AccessAPI ServerAccessMode = "api" )
type ServerConnectivity ¶
type ServerConnectivity string
ServerConnectivity is how the platform reaches apps running on a node.
const ( // ConnectivityPortForward: the central proxy forwards to the node's published // host port (http://<address>:<hostPort>). For trusted/private networks. ConnectivityPortForward ServerConnectivity = "port-forward" // ConnectivityEdgeGateway: the node runs its own gateway (public ingress, its // own TLS) that pulls its routes from the control plane's HTTP provider. ConnectivityEdgeGateway ServerConnectivity = "edge-gateway" )
type ServerRole ¶
type ServerRole string
ServerRole is a node's role in the cluster.
const ( // RoleManager is the control-plane node that runs Miabi itself (the // platform manager). There is exactly one, and it reaches Docker via its local // socket. RoleManager ServerRole = "manager" // RoleNode is a worker node that only runs workloads. RoleNode ServerRole = "node" )
type ServerStatus ¶
type ServerStatus string
ServerStatus is the reachability state of a node.
const ( ServerStatusOnline ServerStatus = "online" ServerStatusOffline ServerStatus = "offline" ServerStatusUnknown ServerStatus = "unknown" )
type ServiceUpdateConfig ¶
type ServiceUpdateConfig struct {
Parallelism int `json:"parallelism,omitempty"`
DelaySeconds int `json:"delay_seconds,omitempty"`
}
ServiceUpdateConfig tunes how Swarm rolls out a service update for cluster apps (parallelism = tasks updated at once; delay = pause between batches).
type Session ¶
type Session struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index;not null"`
JTI string `json:"jti" gorm:"uniqueIndex;not null;size:36"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent" gorm:"size:512"`
Revoked bool `json:"revoked" gorm:"default:false;not null"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
Session tracks an active JWT session for a user (revocable via its JTI).
type Setting ¶
type Setting struct {
ID uint `json:"id" gorm:"primaryKey"`
Key string `json:"key" gorm:"uniqueIndex;not null"`
Value string `json:"value" gorm:"type:text"`
Type SettingType `json:"type" gorm:"default:string;not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Setting is a single global, platform-wide key/value configuration entry. The value is always stored as a string; Type records how to parse it.
type SettingType ¶
type SettingType string
SettingType describes how a setting's string value should be interpreted.
const ( SettingTypeString SettingType = "string" SettingTypeInt SettingType = "int" SettingTypeBool SettingType = "bool" SettingTypeJSON SettingType = "json" )
type Stack ¶
type Stack struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_stack_workspace_name,unique;not null"`
// Name is the unique slug handle scoped to the workspace.
Name string `json:"name" gorm:"index:idx_stack_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI; not unique.
DisplayName string `json:"display_name"`
DockerName string `json:"docker_name" gorm:"uniqueIndex;not null"`
// DockerNetwork is the platform-managed Docker network shared by the stack's
// apps, giving them service-name DNS discovery isolated from other stacks.
DockerNetwork string `json:"docker_network,omitempty"`
Description string `json:"description,omitempty"`
// Metadata holds free-form labels; "miabi.io/" keys are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
// Annotations holds free-form, non-identifying descriptive metadata (the
// manifest's metadata.annotations); no reserved keys. Persisted as JSON.
Annotations Metadata `json:"annotations,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Apps are the applications assigned to this stack. Populated on the stack
// detail view; an application belongs to at most one stack.
Apps []Application `json:"apps,omitempty" gorm:"foreignKey:StackID"`
}
Stack groups related applications in a workspace so they can be managed together. The user-facing Name is unique per workspace; DockerName is the platform-managed Docker Compose project name applied to member containers as the `com.docker.compose.project` label, so Docker tooling groups them too.
type StackEnvVar ¶
type StackEnvVar struct {
ID uint `json:"id" gorm:"primaryKey"`
StackID uint `json:"stack_id" gorm:"index:idx_stack_env_key,unique;not null"`
Key string `json:"key" gorm:"index:idx_stack_env_key,unique;not null"`
Value string `json:"value"` // plaintext for non-secret; ciphertext when IsSecret
IsSecret bool `json:"is_secret" gorm:"not null;default:false"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
StackEnvVar is a shared environment variable injected into every member application's containers at deploy time. App-level vars with the same key take precedence. Secret values are encrypted at rest.
type SystemRole ¶
type SystemRole string
SystemRole is a platform-wide role (distinct from per-workspace roles).
const ( SystemRoleAdmin SystemRole = "admin" // platform super-admin SystemRoleUser SystemRole = "user" )
type Template ¶
type Template struct {
ID uint `json:"id" gorm:"primaryKey"`
SourceID uint `json:"source_id" gorm:"index:idx_template_src_name_ver,unique;not null"`
// Name is the stable template handle (e.g. "ghost").
Name string `json:"name" gorm:"index:idx_template_src_name_ver,unique;not null"`
// Version is the manifest semver; unique per (source, name).
Version string `json:"version" gorm:"index:idx_template_src_name_ver,unique;not null"`
// DisplayName is the free-text catalog label (e.g. "Ghost").
DisplayName string `json:"display_name"`
Category string `json:"category,omitempty"`
Icon string `json:"icon,omitempty"`
Digest string `json:"digest,omitempty"`
RawYAML string `json:"-" gorm:"type:text"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
Template is one version of a catalog entry, synced from a source. RawYAML is the manifest as authored; the parsed form is re-derived on demand.
type TemplateInstall ¶
type TemplateInstall struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
Source string `json:"source"` // official | community | custom
// TemplateName is the template handle installed from (references Template.Name).
TemplateName string `json:"template_name" gorm:"index;not null"`
// TemplateDisplayName is the catalog label snapshot at install time.
TemplateDisplayName string `json:"template_display_name"`
Version string `json:"version"`
StackID *uint `json:"stack_id,omitempty" gorm:"index"`
AppIDs []uint `json:"app_ids,omitempty" gorm:"serializer:json"`
DatabaseIDs []uint `json:"database_ids,omitempty" gorm:"serializer:json"`
VolumeIDs []uint `json:"volume_ids,omitempty" gorm:"serializer:json"`
// Inputs holds the non-secret install answers, for re-install/upgrade prompts.
Inputs map[string]string `json:"inputs,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
}
TemplateInstall records that a stack/app(s) were installed from a template, so the UI can show provenance ("installed from Ghost 1.2.0") and later offer upgrade/uninstall.
type TemplateSource ¶
type TemplateSource struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID *uint `json:"workspace_id,omitempty" gorm:"index"`
Name string `json:"name" gorm:"not null"`
Type TemplateSourceType `json:"type" gorm:"not null"`
URL string `json:"url,omitempty"`
Ref string `json:"ref,omitempty"` // git branch/tag
GitRepoID *uint `json:"git_repo_id,omitempty"` // optional stored credential
Official bool `json:"official" gorm:"not null;default:false"`
Verified bool `json:"verified" gorm:"not null;default:false"`
SyncStatus string `json:"sync_status,omitempty"` // idle | syncing | error
LastSynced *time.Time `json:"last_synced,omitempty"`
LastError string `json:"last_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
TemplateSource is where templates come from. Global sources (WorkspaceID nil) are admin-managed; a custom source is per-workspace.
type TemplateSourceType ¶
type TemplateSourceType string
TemplateSourceType is where a set of templates comes from.
const ( TemplateSourceBuiltin TemplateSourceType = "builtin" // embedded official catalog TemplateSourceGit TemplateSourceType = "git" // a Git repo with an index.yaml TemplateSourceHTTP TemplateSourceType = "http" // a remote index.yaml TemplateSourceCustom TemplateSourceType = "custom" // per-workspace user imports )
type TwoFactorRecoveryCode ¶
type TwoFactorRecoveryCode struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index;not null"`
CodeHash string `json:"-" gorm:"uniqueIndex;not null"`
UsedAt *time.Time `json:"used_at"`
CreatedAt time.Time `json:"created_at"`
}
TwoFactorRecoveryCode is a single-use backup code for two-factor authentication. Only the sha256 hash of the code is stored; the plaintext is shown to the user once at generation time.
type UIDModel ¶
type UIDModel struct {
UID string `json:"uid" gorm:"type:uuid;uniqueIndex;not null;default:gen_random_uuid()"`
}
UIDModel is an embeddable mixin giving a resource a stable, globally-unique, opaque identifier (UUIDv7) alongside its uint primary key. The PK stays the internal handle (foreign keys, Docker labels, performance); the uid is the portable, external one — the Terraform resource ID, the GitOps/backup cross-install reference, and the non-enumerable handle in public URLs.
Embed it anonymously so `obj.UID` is the string directly and JSON exposes "uid". New rows get a UUIDv7 from BeforeCreate (time-ordered); the gen_random_uuid() default backfills pre-existing rows when AutoMigrate adds the column and is a safety net if the hook ever doesn't run.
type UpdateStatus ¶
type UpdateStatus struct {
ID uint `json:"-" gorm:"primaryKey"`
// LatestVersion is the newest release for the running build's channel, as a
// semver tag with the leading "v" (e.g. "v1.0.0-beta.5"). Empty until the
// first successful check.
LatestVersion string `json:"latest_version"`
ReleaseURL string `json:"release_url"`
PublishedAt *time.Time `json:"published_at"`
// ETag from the last successful GitHub response. Replayed as If-None-Match so
// an unchanged release list answers 304, which costs no API quota.
ETag string `json:"-"`
// CheckedVersion is the running build that produced LatestVersion. The verdict
// depends on BOTH the release list and the version we compare it against, but
// the ETag only fingerprints the list — so after an upgrade the list is
// unchanged, GitHub answers 304, and a verdict computed for the OLD build would
// be kept forever. That is how an install could be told "v1.2.1 is available"
// while running 1.3.0. Storing it lets Check notice the build moved and redo
// the comparison.
CheckedVersion string `json:"-"`
// CheckedAt is the last *attempt*; LastError explains a failing one. Both are
// admin-visible so a silently broken checker cannot masquerade as "up to date".
CheckedAt *time.Time `json:"checked_at"`
LastError string `json:"last_error,omitempty"`
// DismissedVersion is the version an admin chose to stop being notified about.
// Platform-wide: only platform admins ever see the notice.
DismissedVersion string `json:"dismissed_version,omitempty"`
UpdatedAt time.Time `json:"-"`
}
UpdateStatus caches the result of the daily release check. Exactly one row (ID 1) ever exists: it is platform state, not a user-editable setting, so it deliberately lives outside the `settings` table — every row there is listed and editable on the admin Settings page.
type UpgradeProgress ¶
type UpgradeProgress struct {
FromVersion string `json:"from_version"`
ToVersion string `json:"to_version"`
Path string `json:"path"` // "in-place" | "dump-restore"
Phase string `json:"phase"` // backing-up | stopping-apps | swapping | dumping | restoring | cutover | verifying | starting-apps | done | failed
Error string `json:"error,omitempty"`
}
UpgradeProgress is the live state of an in-flight version upgrade, surfaced on the instance while Status is "upgrading" (and briefly after, on failure).
type UpgradeStep ¶
type UpgradeStep struct {
ID uint `json:"id" gorm:"primaryKey"`
Version string `json:"version" gorm:"index;not null"`
Name string `json:"name" gorm:"uniqueIndex;not null"`
AppliedAt time.Time `json:"applied_at"`
}
UpgradeStep records an applied, versioned data-upgrade step so it runs at most once. Schema changes use GORM AutoMigrate; data backfills use steps.
type User ¶
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
// Name is the free-text display name.
Name string `json:"name" gorm:"not null;default:''"`
// Username is the unique, directory-friendly handle (lowercase [a-z0-9-]),
// the join key a future LDAP/OIDC uid maps onto and a potential per-user
// registry namespace. Distinct from Email (the login/contact). Auto-derived
// from the email local-part on create when left blank (see BeforeCreate).
Username string `json:"username" gorm:"uniqueIndex;not null"`
Email string `json:"email" gorm:"uniqueIndex;not null"`
PasswordHash string `json:"-" gorm:"not null"`
Role SystemRole `json:"role" gorm:"default:user;not null"`
// TwoFactorSecret holds the TOTP secret, encrypted at rest (crypto package).
// Never serialized. TwoFactorEnabled is only set once a code is confirmed.
TwoFactorSecret string `json:"-" gorm:"type:text"`
TwoFactorEnabled bool `json:"two_factor_enabled" gorm:"default:false;not null"`
Active bool `json:"active" gorm:"default:true;not null"`
MustChangePassword bool `json:"must_change_password" gorm:"not null;default:false"`
// AuthSource records how the account authenticates: "local" (password), or an
// external directory/IdP ("ldap", "saml", "oauth"). Directory-managed accounts
// carry an unusable local password and have their access reconciled on login.
AuthSource string `json:"auth_source" gorm:"not null;default:'local'"`
// WorkspaceLimit is an Enterprise per-user override of how many workspaces
// this user may own, superseding the platform-global max_workspaces_per_user.
// nil = inherit the platform limit; -1 = unlimited; 0 = none; N = at most N.
// Only a platform admin sets it, and only with the user_workspace_limit
// entitlement (existing overrides stay enforced read-only if the license lapses).
WorkspaceLimit *int `json:"workspace_limit,omitempty"`
// WorkspaceMembershipLimit is the Enterprise per-user override of how many
// workspaces this user may JOIN as a non-owner member (invites + SSO/SCIM
// auto-join), superseding the platform-global max_workspace_memberships_per_user.
// Same convention: nil = inherit; -1 = unlimited; 0 = none; N = at most N.
// Gated by the user_workspace_membership_limit entitlement.
WorkspaceMembershipLimit *int `json:"workspace_membership_limit,omitempty"`
// ScheduledDeletionAt, when set, is the time this account is permanently
// purged (with all its data). The admin schedules it; a daily job purges due
// accounts; an admin can cancel before then. The account stays disabled
// (Active=false) throughout the grace window.
ScheduledDeletionAt *time.Time `json:"scheduled_deletion_at" gorm:"index"`
EmailVerifiedAt *time.Time `json:"email_verified_at"`
LastLoginAt *time.Time `json:"last_login_at"`
// OnboardingDismissedAt, when set, is when the user dismissed (or completed)
// the getting-started checklist. Nil = still show onboarding guidance.
OnboardingDismissedAt *time.Time `json:"onboarding_dismissed_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
User is a global identity.
func (*User) BeforeCreate ¶
BeforeCreate derives a unique username when one was not supplied, so every creation path (seed, admin-create, OAuth/SAML auto-provision, SCIM) gets a valid handle without repeating the logic. The base is the email local-part, slugified; collisions and reserved words are resolved by numeric suffixing. An explicitly supplied username is normalized but otherwise trusted (callers validate user-chosen handles up front).
type Volume ¶
type Volume struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_vol_workspace_name,unique;not null"`
// Name is the unique, URL/CLI/docker handle (lowercase [a-z0-9-]) scoped to the
// workspace. Renamed from the former "slug".
Name string `json:"name" gorm:"index:idx_vol_workspace_name,unique;not null"`
// DisplayName is the free-text label shown in the UI. Renamed from "name".
DisplayName string `json:"display_name"`
DockerName string `json:"docker_name" gorm:"uniqueIndex;not null"`
// ServerID is the node this volume lives on (0 = local control-plane node).
ServerID uint `json:"server_id" gorm:"index;not null;default:0"`
// ServerName is the display name of the node (transient; populated on read).
ServerName string `json:"server_name,omitempty" gorm:"-"`
Mountpoint string `json:"mountpoint,omitempty"`
// SizeBytes is the declared capacity / size limit of the volume in bytes
// (0 = unspecified/unlimited). Recorded at create time and used for quota
// accounting; hard enforcement depends on the node's storage backend.
SizeBytes int64 `json:"size_bytes" gorm:"not null;default:0"`
// UsedBytes is the last MEASURED on-disk usage (docker system df), distinct
// from the declared SizeBytes. 0 with nil UsedMeasuredAt = never measured.
UsedBytes int64 `json:"used_bytes" gorm:"not null;default:0"`
UsedMeasuredAt *time.Time `json:"used_measured_at,omitempty"`
// Imported marks a volume that references a pre-existing external Docker volume
// by its own name (DockerName = the existing name; no data was moved/created).
Imported bool `json:"imported" gorm:"not null;default:false"`
// Shared storage (cluster mode). Driver is the Docker volume driver: "local"
// (default, node-local) or "nfs"/"cifs" (a backend a replicated service can
// share across nodes). AccessMode follows from it: local => rwo, shared => rwx.
Driver string `json:"driver" gorm:"not null;default:local"`
// AccessMode is rwo (node-local, single node) or rwx (shared, multi-node). The
// guardrails use it to refuse replicating an app backed by an rwo volume.
AccessMode VolumeAccessMode `json:"access_mode" gorm:"not null;default:rwo"`
// DriverOptsEnc is the encrypted JSON of the driver's mount options (which may
// include a CIFS password). Set at create time, never returned. The volume is
// immutable, so options can't be edited after creation.
DriverOptsEnc string `json:"-" gorm:"type:text"`
// HostPath is the operator-managed host directory a "host" driver volume binds
// (under /mnt/*). Empty for every other driver. Not a secret — it is the bind
// source, mounted directly into the app's containers on each node.
HostPath string `json:"host_path,omitempty"`
// Metadata holds free-form labels; "miabi.io/" keys are platform-managed.
Metadata Metadata `json:"metadata,omitempty" gorm:"serializer:json"`
// Annotations holds free-form, non-identifying descriptive metadata (the
// manifest's metadata.annotations); no reserved keys. Persisted as JSON.
Annotations Metadata `json:"annotations,omitempty" gorm:"serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Volume is a workspace-owned, managed Docker volume for persistent data.
type VolumeAccessMode ¶
type VolumeAccessMode string
VolumeAccessMode is how many nodes can mount a volume read-write at once.
const ( // AccessRWO (ReadWriteOnce): a node-local volume usable by one node at a time // (the default "local" driver). A replicated service must NOT mount one, or // each node would silently get its own empty copy. AccessRWO VolumeAccessMode = "rwo" // AccessRWX (ReadWriteMany): a shared volume (NFS/CIFS/Ceph) every replica can // mount at once, so a stateful service can run with replicas > 1. AccessRWX VolumeAccessMode = "rwx" )
type VolumeBackup ¶
type VolumeBackup struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
VolumeID uint `json:"volume_id" gorm:"index;not null"`
ServerID uint `json:"server_id" gorm:"index;not null;default:0"` // node the volume lives on
VolumeName string `json:"volume_name"` // docker volume name
Status BackupStatus `json:"status" gorm:"not null;default:pending"`
Trigger string `json:"trigger"` // manual | scheduled
S3Bucket string `json:"s3_bucket,omitempty"`
S3Path string `json:"s3_path,omitempty"` // remote folder prefix used
Filename string `json:"filename,omitempty"` // archive object name
SizeBytes int64 `json:"size_bytes"`
// Logs is a bounded tail of the backup output for instant display; the full
// log lives in the log store at LogRef once the run is terminal (see
// plans/log-storage.md). LogRef is empty when the store is disabled or the
// row predates externalization — readers fall back to this tail.
Logs string `json:"logs,omitempty" gorm:"type:text"`
LogRef string `json:"log_ref,omitempty"`
LogBytes int64 `json:"log_bytes,omitempty"`
LogLines int `json:"log_lines,omitempty"`
LogTruncated bool `json:"log_truncated,omitempty"`
Error string `json:"error,omitempty" gorm:"type:text"`
StartedAt *time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `json:"created_at"`
}
VolumeBackup is a single backup run of a managed volume's contents, archived (compressed) to the workspace's S3 target. Reuses BackupStatus.
type Webhook ¶
type Webhook struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
Name string `json:"name"`
URL string `json:"url" gorm:"not null"`
Secret string `json:"-" gorm:"not null"`
Events []string `json:"events" gorm:"serializer:json"`
// Headers are extra HTTP headers sent with each delivery (e.g. an
// Authorization header for the receiving service).
Headers map[string]string `json:"headers,omitempty" gorm:"serializer:json"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// HasSecret is a transient flag for responses (never persisted).
HasSecret bool `json:"has_secret" gorm:"-"`
// PlainSecret carries the generated secret in the create response only.
PlainSecret string `json:"secret,omitempty" gorm:"-"`
}
Webhook is a workspace-registered HTTP endpoint that receives a signed JSON payload when a subscribed application event fires. The signing Secret is encrypted at rest and never serialized (a transient HasSecret flag is exposed instead); it is returned in cleartext exactly once, at creation time.
type WebhookDelivery ¶
type WebhookDelivery struct {
ID uint `json:"id" gorm:"primaryKey"`
WebhookID uint `json:"webhook_id" gorm:"index;not null"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
Event string `json:"event" gorm:"not null"`
// EventID is the source AppEvent, enabling exact redelivery. 0 for tests.
EventID uint `json:"event_id,omitempty"`
Status WebhookDeliveryStatus `json:"status" gorm:"type:varchar(16);not null;index"`
HTTPStatusCode int `json:"http_status_code"`
ErrorMessage string `json:"error_message,omitempty"`
Attempt int `json:"attempt" gorm:"not null;default:1"`
CreatedAt time.Time `json:"created_at"`
}
WebhookDelivery records the outcome of delivering one event to one webhook. One row is written per attempt; Attempt is the 1-based attempt number.
type WebhookDeliveryStatus ¶
type WebhookDeliveryStatus string
WebhookDeliveryStatus is the terminal outcome of a delivery attempt chain.
const ( WebhookDeliverySuccess WebhookDeliveryStatus = "success" WebhookDeliveryFailed WebhookDeliveryStatus = "failed" )
type Workspace ¶
type Workspace struct {
UIDModel
ID uint `json:"id" gorm:"primaryKey"`
// Name is the unique, URL/CLI/docker handle (lowercase [a-z0-9-]). It is the
// human key for scoped routes and the registry namespace. Renamed from the
// former "slug"; the numeric ID/UID remain the stable internal references.
Name string `json:"name" gorm:"uniqueIndex;not null"`
// DisplayName is the free-text label shown in the UI. Renamed from the former
// "name"; not unique.
DisplayName string `json:"display_name"`
Description string `json:"description"`
OwnerID uint `json:"owner_id" gorm:"index;not null"`
// OrganizationID is the realm this workspace belongs to (nullable → the
// default org). Lets SSO/enforced-login/SCIM scope to an org without a
// destructive migration when multi-org lands.
OrganizationID *uint `json:"organization_id" gorm:"index"`
// Privileged grants trusted capabilities — currently, host port-binding
// requests are auto-approved (range and conflict checks still apply). Only a
// platform admin can set it.
Privileged bool `json:"privileged" gorm:"not null;default:false"`
// System marks the built-in platform workspace ("Miabi System"). It holds
// platform-managed apps (e.g. the per-node Goma gateways), is created on
// first boot, is always privileged, and cannot be deleted. Only platform
// admins manage it.
System bool `json:"system" gorm:"not null;default:false"`
// PlanID is the assigned plan (nil → the default plan → unlimited). Drives
// per-workspace resource quotas when plan enforcement is enabled.
PlanID *uint `json:"plan_id,omitempty" gorm:"index"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Members []WorkspaceMember `json:"-" gorm:"foreignKey:WorkspaceID"`
}
Workspace is the multi-tenant root that owns all resources.
type WorkspaceBackupSettings ¶
type WorkspaceBackupSettings struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"uniqueIndex;not null"`
S3Enabled bool `json:"s3_enabled"`
S3Endpoint string `json:"s3_endpoint,omitempty"`
S3Bucket string `json:"s3_bucket,omitempty"`
S3Region string `json:"s3_region,omitempty"`
S3AccessKey string `json:"s3_access_key,omitempty"`
S3SecretKeyEnc string `json:"-" gorm:"column:s3_secret_key_enc"` // encrypted at rest
S3UseSSL bool `json:"s3_use_ssl"`
S3ForcePathStyle bool `json:"s3_force_path_style"`
// Path prefixes within the bucket (one shared bucket, two prefixes).
DatabaseBackupPath string `json:"database_backup_path,omitempty"`
VolumeBackupPath string `json:"volume_backup_path,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// S3SecretSet reports whether a secret key is stored, without exposing it.
// Not persisted; populated on read so the UI can render "••••• (set)".
S3SecretSet bool `json:"s3_secret_set" gorm:"-"`
}
WorkspaceBackupSettings holds a workspace's shared backup target: one S3-compatible bucket + credentials, plus the path prefixes under which database and volume backups are stored. Both database and volume backups read this single config. The secret key is encrypted at rest and never returned.
type WorkspaceInvitation ¶
type WorkspaceInvitation struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index;not null"`
Email string `json:"email" gorm:"not null;index"`
Role WorkspaceRole `json:"role" gorm:"not null;default:viewer"`
TokenHash string `json:"-" gorm:"uniqueIndex;not null"`
Status InvitationStatus `json:"status" gorm:"not null;default:pending"`
InvitedBy uint `json:"invited_by" gorm:"not null"`
ExpiresAt time.Time `json:"expires_at" gorm:"not null"`
CreatedAt time.Time `json:"created_at"`
}
WorkspaceInvitation is a pending invite for an email to join a workspace.
type WorkspaceKey ¶
type WorkspaceKey struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"index:idx_wskey_ws_ver,unique;not null"`
Version int `json:"version" gorm:"index:idx_wskey_ws_ver,unique;not null"`
// WrappedDEK is the DEK encrypted (AES-GCM) under the master KEK — i.e.
// crypto.Encrypt(base64(dek)). Never returned by the API.
WrappedDEK string `json:"-" gorm:"type:text;not null"`
// Active marks the version new writes encrypt with. Older versions are kept so
// existing ciphertext stays decryptable until a rotation sweep migrates it.
Active bool `json:"active" gorm:"not null;default:true;index"`
CreatedAt time.Time `json:"created_at"`
RotatedAt time.Time `json:"rotated_at"`
}
WorkspaceKey is a workspace's data-encryption key (DEK), stored wrapped by the master KEK (MIABI_ENCRYPTION_KEY). The DEK encrypts the workspace's secrets at rest; the KEK only ever wraps the DEK. Keys are versioned: rotation creates a new active version and re-encrypts the workspace's data to it, then retires the old one. Exactly one version per workspace is Active (the one new writes use).
type WorkspaceMember ¶
type WorkspaceMember struct {
ID uint `json:"id" gorm:"primaryKey"`
WorkspaceID uint `json:"workspace_id" gorm:"uniqueIndex:idx_workspace_user;not null"`
UserID uint `json:"user_id" gorm:"uniqueIndex:idx_workspace_user;not null"`
Role WorkspaceRole `json:"role" gorm:"not null;default:viewer"`
// CustomRoleID, when set, overrides Role with an admin-defined permission set
// (Enterprise). Role still holds the custom role's BaseRole for rank checks.
CustomRoleID *uint `json:"custom_role_id" gorm:"index"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
User User `json:"user" gorm:"foreignKey:UserID"`
}
WorkspaceMember links a user to a workspace with a role.
type WorkspaceQuota ¶
type WorkspaceQuota struct {
WorkspaceID uint `json:"workspace_id" gorm:"primaryKey"`
MaxApps *int `json:"max_apps,omitempty"`
MaxDatabaseInstances *int `json:"max_database_instances,omitempty"`
MaxCronJobs *int `json:"max_cron_jobs,omitempty"`
MaxVolumes *int `json:"max_volumes,omitempty"`
MaxNetworks *int `json:"max_networks,omitempty"`
MaxAPIKeys *int `json:"max_api_keys,omitempty"`
MaxMembers *int `json:"max_members,omitempty"`
MaxDatabasesPerInstance *int `json:"max_databases_per_instance,omitempty"`
MaxCPUCores *int `json:"max_cpu_cores,omitempty"`
MaxMemoryMB *int `json:"max_memory_mb,omitempty"`
MaxDatabaseInstanceSizeMB *int `json:"max_database_instance_size_mb,omitempty"`
MaxStorageMB *int `json:"max_storage_mb,omitempty"`
MaxRunners *int `json:"max_runners,omitempty"`
MaxGPUs *int `json:"max_gpus,omitempty"`
AllowCustomTLS *bool `json:"allow_custom_tls,omitempty"`
AllowPrivilegedHostMounts *bool `json:"allow_privileged_host_mounts,omitempty"`
AllowShellExec *bool `json:"allow_shell_exec,omitempty"`
AllowDNSProviders *bool `json:"allow_dns_providers,omitempty"`
AllowCustomLabels *bool `json:"allow_custom_labels,omitempty"`
AllowPlatformRunners *bool `json:"allow_platform_runners,omitempty"`
AllowCustomBuilder *bool `json:"allow_custom_builder,omitempty"`
AllowGPU *bool `json:"allow_gpu,omitempty"`
SecurityProfile *string `json:"security_profile,omitempty"` // nil = inherit plan
AllowOfficialImageUser *bool `json:"allow_official_image_user,omitempty"` // nil = inherit plan
UpdatedAt time.Time `json:"updated_at"`
}
WorkspaceQuota holds per-workspace overrides applied on top of the assigned plan. Any non-nil field overrides the plan for that workspace; nil inherits.
type WorkspaceRole ¶
type WorkspaceRole string
WorkspaceRole is a member's role within a workspace.
const ( WorkspaceRoleOwner WorkspaceRole = "owner" WorkspaceRoleAdmin WorkspaceRole = "admin" WorkspaceRoleDeveloper WorkspaceRole = "developer" WorkspaceRoleViewer WorkspaceRole = "viewer" )
func (WorkspaceRole) AtLeast ¶
func (r WorkspaceRole) AtLeast(min WorkspaceRole) bool
AtLeast reports whether r is at least as privileged as min.
func (WorkspaceRole) Rank ¶
func (r WorkspaceRole) Rank() int
Rank returns the privilege level of a role (0 if unknown).
func (WorkspaceRole) Valid ¶
func (r WorkspaceRole) Valid() bool
Valid reports whether r is a known role.
type WorkspaceWithRole ¶
type WorkspaceWithRole struct {
Workspace
Role WorkspaceRole `json:"role"`
}
WorkspaceWithRole is a workspace annotated with the requesting user's role in it. Returned by the list endpoint so the UI can render role badges and gate admin-only affordances without an extra round-trip per workspace.
Source Files
¶
- acme_account.go
- alert.go
- analytics.go
- api_key.go
- app_event.go
- application.go
- audit_log.go
- backup.go
- certificate.go
- custom_role.go
- database.go
- dns_provider.go
- dns_record.go
- domain.go
- environment.go
- git_repository.go
- gitsource.go
- gpu.go
- image.go
- inbox.go
- job.go
- ldap.go
- license.go
- metadata.go
- metric.go
- middleware.go
- network.go
- network_allocation.go
- notification.go
- oauth_provider.go
- organization.go
- password_reset.go
- permission.go
- pipeline.go
- plan.go
- platform_backup.go
- port.go
- registry.go
- registry_settings.go
- resource_policy.go
- route.go
- runner.go
- saml.go
- scim.go
- secret.go
- server.go
- session.go
- setting.go
- siem.go
- stack.go
- template.go
- two_factor_recovery_code.go
- uid.go
- update_status.go
- upgrade_step.go
- user.go
- volume.go
- volume_backup.go
- webhook.go
- workspace.go
- workspace_backup_settings.go
- workspace_key.go