Documentation
¶
Overview ¶
Package kitio imports Git-owned stackkit.yaml definitions and generates reviewable CUE, Terraform, Docker Compose, and YAML artifacts.
Index ¶
- Variables
- func CanonicalHash(def KitDefinition) (string, error)
- func ExportCUE(def KitDefinition) (stackfileBytes, servicesBytes []byte, err error)
- func ExportCompose(def KitDefinition, outDir string) error
- func ExportTerraform(def KitDefinition, outDir string) error
- func ExportYAML(def KitDefinition) ([]byte, error)
- func SortedModeKeys(m map[string]ModeDef) []string
- type AddonsDef
- type ApplicationConnectorDef
- type ApplicationDef
- type ApplicationProductAPIDef
- type ApplicationRILCapabilityDef
- type ApplicationRILDef
- type ApplicationRuntimeProfileDef
- type ChangelogEntry
- type ComputeTierDef
- type FieldDifference
- type FoundationDef
- type KitDefinition
- type KitMetadata
- type ModeDef
- type NodeTypeDef
- type PlatformDef
- type PlatformField
- type ResourceRequirements
- type ReverseMapping
- type RoundTripReport
- type SectionKind
- type ServiceSpecDef
Constants ¶
This section is empty.
Variables ¶
var ApplicationToGroup = map[string]string{
"photos": "photo-management",
"media": "media-streaming",
"vault": "password-vault",
"smart-home": "smart-home",
"files": "file-sharing",
"ai": "ai-workloads",
"dev": "dev-platform",
"mail": "mail-server",
"game": "game-server",
"remote": "remote-desktop",
}
ApplicationToGroup maps stackkit.yaml `application.<key>` to sk_service_group.slug. Pre-2026-04 named UseCaseToGroup with key `useCases.<key>`. See migration 000084.
var FoundationToGroup = map[string]string{
"security-baseline": "security-baseline",
"admin-bootstrap": "admin-bootstrap",
}
FoundationToGroup maps stackkit.yaml `foundation.<key>` to sk_service_group.slug.
Note (migration 000085): `login-gateway` was previously here. Per ARCHITECTURE_V6 §4, login-gateway (TinyAuth + PocketID) is L2 Platform, not L1 Foundation — the admin bootstrap step is Foundation, the identity service itself is Platform. login-gateway moved to PlatformToGroup.
var PlatformToGroup = map[string]string{
"traefik": "reverse-proxy",
"tinyauth": "forward-auth",
"login-gateway": "forward-auth",
"pocketid": "oidc-provider",
"paas": "paas",
"monitoring": "uptime-monitoring",
"dashboard": "dashboard",
"dozzle": "container-logs",
"socket-proxy": "docker-socket-proxy",
"unbound": "dns-resolver",
"adguard-home": "dns-filter",
"crowdsec": "intrusion-detection",
}
PlatformToGroup maps stackkit.yaml `platform.<key>` to sk_service_group.slug.
Functions ¶
func CanonicalHash ¶
func CanonicalHash(def KitDefinition) (string, error)
CanonicalHash returns sha256(canonical-json(def)) — same algorithm as the kit-import endpoint server-side. Used for drift detection + golden tests.
func ExportCUE ¶
func ExportCUE(def KitDefinition) (stackfileBytes, servicesBytes []byte, err error)
ExportCUE renders a kit definition into CUE form.
Two outputs:
- stackfileBytes: package <slug> with #StackKitConfig including metadata + supportedOS + modes + features.
- servicesBytes: package <slug> with the per-service-group selections and per-tier sizing as commented-out placeholder records (the actual module CUE definitions live in modules/*/module.cue and are not duplicated here).
This is a *valid CUE* representation of the DB-shape — it is NOT meant to byte-equal the hand-written basement-kit/stackfile.cue, which carries hand-curated comments + schema constraints. The roundtrip test validates `cue eval` succeeds, not byte-equality.
func ExportCompose ¶
func ExportCompose(def KitDefinition, outDir string) error
ExportCompose produces a Docker-Compose-style overview file at the kit level — NOT per-module compose fragments (those are generated by stackkit generate at deploy-time from the StackSpec + selected modules).
What this writes is a `kit-overview.compose.yml` that records the kit's service-group selections in compose-comment form, useful for operators who want to read what a kit "is" without running stackkit generate.
func ExportTerraform ¶
func ExportTerraform(def KitDefinition, outDir string) error
ExportTerraform produces a `terraform.tfvars.json` from the kit definition's compute-tier requirements + selections + features.
This is intentionally a thin "kit-level" tfvars surface:
- tier sizing -> tfvars (cpu, memory, disk per tier)
- selected modules per group -> tfvars enable_<module> bool
- features -> tfvars feature_<name> bool
Per-module Terraform fragments are still generated by the existing internal/cue Generator at deploy-time from a StackSpec, not here. This export covers the kit-definition contract surface only — what a downstream installer needs to know about the kit's defaults.
func ExportYAML ¶
func ExportYAML(def KitDefinition) ([]byte, error)
ExportYAML produces a stackkit.yaml byte representation of the kit definition. The output is structurally equivalent to the original stackkit.yaml — same sections, same keys, same nesting — modulo cosmetic ordering and whitespace.
Used by:
- stackkit kit export (writes to disk)
- stackkit kit roundtrip (compares against original)
- kit_export_test.go golden tests
func SortedModeKeys ¶
SortedModeKeys returns mode keys in stable order.
Types ¶
type AddonsDef ¶
type AddonsDef struct {
AutoActivated []string `yaml:"autoActivated,omitempty" json:"autoActivated,omitempty"`
Optional []string `yaml:"optional,omitempty" json:"optional,omitempty"`
}
AddonsDef holds autoActivated + optional addon slug lists.
type ApplicationConnectorDef ¶
type ApplicationConnectorDef struct {
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Owner string `yaml:"owner,omitempty" json:"owner,omitempty"`
Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
Transport string `yaml:"transport,omitempty" json:"transport,omitempty"`
Auth string `yaml:"auth,omitempty" json:"auth,omitempty"`
NativeProduct bool `yaml:"nativeProduct,omitempty" json:"nativeProduct,omitempty"`
Capabilities []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
}
type ApplicationDef ¶
type ApplicationDef struct {
Role string `yaml:"role" json:"role"`
DefaultTool string `yaml:"defaultTool,omitempty" json:"defaultTool,omitempty"`
Alternatives []string `yaml:"alternatives,omitempty" json:"alternatives,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Package string `yaml:"package,omitempty" json:"package,omitempty"`
DefaultRuntimeProfile string `yaml:"defaultRuntimeProfile,omitempty" json:"defaultRuntimeProfile,omitempty"`
RuntimeProfiles map[string]ApplicationRuntimeProfileDef `yaml:"runtimeProfiles,omitempty" json:"runtimeProfiles,omitempty"`
Connectors map[string]ApplicationConnectorDef `yaml:"connectors,omitempty" json:"connectors,omitempty"`
ProductAPIs map[string]ApplicationProductAPIDef `yaml:"productApis,omitempty" json:"productApis,omitempty"`
RIL *ApplicationRILDef `yaml:"ril,omitempty" json:"ril,omitempty"`
}
ApplicationDef is a user-facing application category (photos, media, ...). Lives under stackkit.yaml `application:` key (canonical L3 layer per ADR-0012). Pre-2026-04 this was named UseCaseDef under `useCases:` — see migration 000084.
type ApplicationProductAPIDef ¶
type ApplicationProductAPIDef struct {
Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"`
BasePath string `yaml:"basePath,omitempty" json:"basePath,omitempty"`
Auth string `yaml:"auth,omitempty" json:"auth,omitempty"`
Purpose string `yaml:"purpose,omitempty" json:"purpose,omitempty"`
}
type ApplicationRILCapabilityDef ¶
type ApplicationRILCapabilityDef struct {
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Authority string `yaml:"authority,omitempty" json:"authority,omitempty"`
Source string `yaml:"source,omitempty" json:"source,omitempty"`
RequiresApproval bool `yaml:"requiresApproval,omitempty" json:"requiresApproval,omitempty"`
Evidence string `yaml:"evidence,omitempty" json:"evidence,omitempty"`
}
type ApplicationRILDef ¶
type ApplicationRILDef struct {
Capabilities map[string]ApplicationRILCapabilityDef `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
}
type ApplicationRuntimeProfileDef ¶
type ApplicationRuntimeProfileDef struct {
DisplayName string `yaml:"displayName,omitempty" json:"displayName,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Realization string `yaml:"realization,omitempty" json:"realization,omitempty"`
PlacementModes []string `yaml:"placementModes,omitempty" json:"placementModes,omitempty"`
Contexts []string `yaml:"contexts,omitempty" json:"contexts,omitempty"`
ManagedServerlessEligible bool `yaml:"managedServerlessEligible,omitempty" json:"managedServerlessEligible,omitempty"`
RequiresControlPlane bool `yaml:"requiresControlPlane,omitempty" json:"requiresControlPlane,omitempty"`
RequiresLocalBridge bool `yaml:"requiresLocalBridge,omitempty" json:"requiresLocalBridge,omitempty"`
Notes []string `yaml:"notes,omitempty" json:"notes,omitempty"`
}
type ChangelogEntry ¶
type ChangelogEntry struct {
Version string `yaml:"version" json:"version"`
Date string `yaml:"date,omitempty" json:"date,omitempty"`
Changes []string `yaml:"changes,omitempty" json:"changes,omitempty"`
}
ChangelogEntry mirrors a YAML changelog item.
type ComputeTierDef ¶
type ComputeTierDef struct {
Requirements ResourceRequirements `yaml:"requirements,omitempty" json:"requirements,omitempty"`
AdditionalServices []string `yaml:"additionalServices,omitempty" json:"additionalServices,omitempty"`
ServiceOverrides map[string]interface{} `yaml:"serviceOverrides,omitempty" json:"serviceOverrides,omitempty"`
PaasOverride string `yaml:"paasOverride,omitempty" json:"paasOverride,omitempty"`
}
ComputeTierDef is a tier sizing entry (low/standard/high).
type FieldDifference ¶
type FieldDifference struct {
Path string `json:"path"`
Severity string `json:"severity"` // "critical" | "cosmetic"
Original interface{} `json:"original,omitempty"`
Reconstructed interface{} `json:"reconstructed,omitempty"`
Note string `json:"note,omitempty"`
}
FieldDifference is a single field-level diff entry.
func Diff ¶
func Diff(a, b KitDefinition) []FieldDifference
Diff produces a list of FieldDifference entries between two KitDefinition values. Tracks the most relevant top-level + nested field paths for roundtrip validation.
type FoundationDef ¶
type FoundationDef struct {
Role string `yaml:"role" json:"role"`
Alternatives []string `yaml:"alternatives,omitempty" json:"alternatives,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
}
FoundationDef is a foundation-layer service slot.
type KitDefinition ¶
type KitDefinition struct {
APIVersion string `yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"`
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
Metadata KitMetadata `yaml:"metadata" json:"metadata"`
SupportedOS []string `yaml:"supportedOS,omitempty" json:"supportedOS,omitempty"`
Requirements map[string]interface{} `yaml:"requirements,omitempty" json:"requirements,omitempty"`
Modes map[string]ModeDef `yaml:"modes,omitempty" json:"modes,omitempty"`
AutoSelect map[string]interface{} `yaml:"autoSelect,omitempty" json:"autoSelect,omitempty"`
Application map[string]ApplicationDef `yaml:"application,omitempty" json:"application,omitempty"`
Foundation map[string]FoundationDef `yaml:"foundation,omitempty" json:"foundation,omitempty"`
Platform PlatformField `yaml:"platform,omitempty" json:"platform,omitempty"`
Features map[string]bool `yaml:"features,omitempty" json:"features,omitempty"`
ComputeTiers map[string]ComputeTierDef `yaml:"computeTiers,omitempty" json:"computeTiers,omitempty"`
Outputs map[string]interface{} `yaml:"outputs,omitempty" json:"outputs,omitempty"`
Changelog []ChangelogEntry `yaml:"changelog,omitempty" json:"changelog,omitempty"`
// Multi-node kit fields
NodeTypes map[string]NodeTypeDef `yaml:"nodeTypes,omitempty" json:"nodeTypes,omitempty"`
Addons AddonsDef `yaml:"addons,omitempty" json:"addons,omitempty"`
Identity map[string]interface{} `yaml:"identity,omitempty" json:"identity,omitempty"`
Pattern map[string]interface{} `yaml:"pattern,omitempty" json:"pattern,omitempty"`
PaaS map[string]interface{} `yaml:"paas,omitempty" json:"paas,omitempty"`
Secrets map[string]interface{} `yaml:"secrets,omitempty" json:"secrets,omitempty"`
// Cluster-oriented kit fields
Swarm map[string]interface{} `yaml:"swarm,omitempty" json:"swarm,omitempty"`
Services []ServiceSpecDef `yaml:"services,omitempty" json:"services,omitempty"`
Extends string `yaml:"extends,omitempty" json:"extends,omitempty"`
// Common optional
Architecture string `yaml:"architecture,omitempty" json:"architecture,omitempty"`
TunnelOptions []string `yaml:"tunnelOptions,omitempty" json:"tunnelOptions,omitempty"`
SecretsProvider string `yaml:"-" json:"secretsProvider,omitempty"`
// Publication fields materialized by the private Git/CUE publisher.
CueSourcePath string `yaml:"-" json:"cueSourcePath,omitempty"`
ContractHash string `yaml:"-" json:"contractHash,omitempty"`
}
KitDefinition is the canonical in-process shape for a Git-owned Kit source.
func Import ¶
func Import(yamlBytes []byte) (KitDefinition, error)
Import parses stackkit.yaml bytes into a KitDefinition.
Two-pass approach:
- yaml.Unmarshal into KitDefinition struct (typed sections).
- Re-parse as generic map to preserve unknown keys (Outputs, Pattern etc. that the struct surfaces as map[string]interface{}).
type KitMetadata ¶
type KitMetadata = models.KitMetadata
KitMetadata is a source-compatible alias to the one concrete Go schema.
type ModeDef ¶
type ModeDef struct {
Description string `yaml:"description,omitempty" json:"description,omitempty"`
TemplateDir string `yaml:"templateDir,omitempty" json:"templateDir,omitempty"`
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
Recommended bool `yaml:"recommended,omitempty" json:"recommended,omitempty"`
Features []string `yaml:"features,omitempty" json:"features,omitempty"`
Requires []string `yaml:"requires,omitempty" json:"requires,omitempty"`
RecommendedFor []string `yaml:"recommended_for,omitempty" json:"recommended_for,omitempty"`
Stacks []map[string]interface{} `yaml:"stacks,omitempty" json:"stacks,omitempty"`
}
ModeDef captures an install/lifecycle mode such as bare, bootstrapped, or advanced.
type NodeTypeDef ¶
type NodeTypeDef struct {
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Access string `yaml:"access,omitempty" json:"access,omitempty"`
Role string `yaml:"role,omitempty" json:"role,omitempty"`
Requirements map[string]interface{} `yaml:"requirements,omitempty" json:"requirements,omitempty"`
Providers []string `yaml:"providers,omitempty" json:"providers,omitempty"`
}
NodeTypeDef describes local/cloud node roles in multi-node kit definitions.
type PlatformDef ¶
type PlatformDef struct {
Role string `yaml:"role" json:"role"`
DefaultTool string `yaml:"defaultTool,omitempty" json:"defaultTool,omitempty"`
Alternatives []string `yaml:"alternatives,omitempty" json:"alternatives,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
}
PlatformDef is a platform-layer service slot.
type PlatformField ¶
type PlatformField struct {
// AsString holds the scalar form, e.g. "docker".
AsString string
// AsMap holds the structured form (basement-kit).
AsMap map[string]PlatformDef
}
PlatformField is a polymorphic yaml `platform:` value.
In basement-kit it is a map of platform service slots:
platform:
traefik: { role: default }
paas: { role: optional, defaultTool: coolify }
In future kit definitions it may be represented as a single string:
platform: docker
We keep both forms reachable.
func (PlatformField) IsEmpty ¶
func (p PlatformField) IsEmpty() bool
IsEmpty reports whether the field carries no data.
func (PlatformField) MarshalJSON ¶
func (p PlatformField) MarshalJSON() ([]byte, error)
MarshalJSON keeps the symmetry for kit-import POST body.
func (PlatformField) MarshalYAML ¶
func (p PlatformField) MarshalYAML() (interface{}, error)
MarshalYAML emits the string form when set, otherwise the map form, otherwise nothing.
func (*PlatformField) UnmarshalJSON ¶
func (p *PlatformField) UnmarshalJSON(b []byte) error
UnmarshalJSON accepts string-or-map for kit-export round-trip.
func (*PlatformField) UnmarshalYAML ¶
func (p *PlatformField) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML accepts both yaml shapes.
type ResourceRequirements ¶
type ResourceRequirements struct {
CPU int `yaml:"cpu,omitempty" json:"cpu,omitempty"`
Memory int `yaml:"memory,omitempty" json:"memory,omitempty"`
Disk int `yaml:"disk,omitempty" json:"disk,omitempty"`
DiskType string `yaml:"diskType,omitempty" json:"diskType,omitempty"`
// Multi-node
ManagerNodes int `yaml:"managerNodes,omitempty" json:"managerNodes,omitempty"`
WorkerNodes int `yaml:"workerNodes,omitempty" json:"workerNodes,omitempty"`
Nodes int `yaml:"nodes,omitempty" json:"nodes,omitempty"`
}
ResourceRequirements captures cpu/memory/disk + optional disk type.
type ReverseMapping ¶
type ReverseMapping struct {
Section SectionKind
Key string
}
ReverseMapping records the original section + key that produced a given service-group slug. Used by export to put selections back into the right stackkit.yaml section.
func GroupReverseMappings ¶
func GroupReverseMappings(groupSlug string) []ReverseMapping
GroupReverseMappings returns the section/key pairs that map to a given service-group slug. Some groups (e.g. "forward-auth") have multiple sources (foundation.login-gateway + platform.tinyauth) — both are returned.
type RoundTripReport ¶
type RoundTripReport struct {
Slug string `json:"slug"`
OriginalHash string `json:"originalHash"`
ReconstructedHash string `json:"reconstructedHash"`
HashesEqual bool `json:"hashesEqual"`
Differences []FieldDifference `json:"differences,omitempty"`
CosmeticOnly bool `json:"cosmeticOnly"`
Formats []string `json:"formats"`
}
RoundTripReport captures the structural diff between the original kit definition and the reconstructed one.
func LocalRoundTrip ¶
func LocalRoundTrip(yamlBytes []byte) (RoundTripReport, error)
LocalRoundTrip runs the YAML-only roundtrip cycle in memory:
- Import original yaml -> KitDefinition A
- Export A -> reconstructed yaml
- Import reconstructed yaml -> KitDefinition B
- Compare A vs B as structs (cosmetic yaml differences ignored)
- Compute hashes; match indicates lossless roundtrip
This is the cheap, no-API check that ships with `stackkit kit roundtrip` and runs in unit tests. The live API counterpart lives in client.go + kit_roundtrip_live_test.go.
type SectionKind ¶
type SectionKind string
SectionKind identifies which yaml section a service-group belongs to.
const ( SectionApplication SectionKind = "application" SectionFoundation SectionKind = "foundation" SectionPlatform SectionKind = "platform" )
func PreferredSection ¶
func PreferredSection(groupSlug string) (SectionKind, string, bool)
PreferredSection picks the canonical section for a group when multiple sources exist. Rule: foundation > platform > application. Within the same section, we prefer the SHORTER key, then lexicographically earlier — this keeps `login-gateway` (the documented canonical) winning over `tinyauth` (the legacy alias) for forward-auth, deterministically.
Migration 000086 (P1-2 fix): login-gateway moved from foundation to platform per ARCHITECTURE_V6 §4. Both login-gateway and tinyauth are now in platform; the deterministic-within-section rule keeps the canonical pair stable.
type ServiceSpecDef ¶
type ServiceSpecDef struct {
Name string `yaml:"name" json:"name"`
Required bool `yaml:"required,omitempty" json:"required,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
Replicas *int `yaml:"replicas,omitempty" json:"replicas,omitempty"`
SwarmMode bool `yaml:"swarmMode,omitempty" json:"swarmMode,omitempty"`
Deploy map[string]interface{} `yaml:"deploy,omitempty" json:"deploy,omitempty"`
}
ServiceSpecDef declares deployment overrides per service.