store

package
v0.4.3 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MaxCapabilities    = 64
	MaxCapabilityBytes = 4096
)

Capabilities are agent-supplied: bounded in count and bytes like enrollment facts.

View Source
const (
	MigrationAnalyzed           = "analyzed"
	MigrationDestinationCreated = "destination_created"
	MigrationValidated          = "validated"
	MigrationCutoverConfirmed   = "cutover_confirmed"
	MigrationAbandoned          = "abandoned"
	MaxMigrationReportBytes     = 65536
	MaxMigrationNoteRunes       = 500
	// MaxDestinationSuffix is the last " (n)" a destination name takes when the ones before it
	// are held.
	MaxDestinationSuffix = 9
)

Migration statuses. A migration is open until cutover_confirmed or abandoned, and a source has at most one open migration. See docs/application-schema.md, Migration.

View Source
const (
	MaxApplicationsPerOrganization = 100
	MaxApplicationRevisions        = 100
	MaxApplicationSpecBytes        = 64 * 1024
)
View Source
const (
	ActionRecreate = "container.recreate"
	ActionRun      = "container.run"
)

Direct commands recreate or run one container from a configuration the operator saw in full. Each is an endpoint_commands row whose frame is an explicit deployment.apply, settled from the agent's deployment.result.

View Source
const (
	ActionWorkloadApply = "workload.apply"
	ActionWorkloadRun   = "workload.run"
)

ActionWorkloadApply and ActionWorkloadRun are direct commands whose frame is a workload.apply (a run's with create set), settled from the agent's deployment.result with service protocol.WorkloadApplyService.

View Source
const (
	PolicyModeApply    = "apply"
	PolicyModePlanOnly = "plan_only"
	PolicyActive       = "active"
	PolicyPaused       = "paused"
	// MinPolicyWindowMinutes is the shortest window. A window never crosses midnight.
	MinPolicyWindowMinutes = 15
	// PolicyFailureLimit consecutive failed windows pause a policy.
	PolicyFailureLimit = 3
	// MaxPolicyRuns bounds one page of runs; the policy read carries the latest policyRunsShown.
	MaxPolicyRuns = 100
)

Update policies: one per application, saved by an organization administrator and run by the scheduler in internal/api as whoever saved it last. See docs/application-schema.md, Update policies.

View Source
const (
	RunSkippedMissed = "skipped_missed"
	RunSkippedBusy   = "skipped_busy"
	RunNoUpdate      = "no_update"
	RunPlanned       = "planned"
	RunApplied       = "applied"
	RunBlocked       = "blocked"
	RunFailed        = "failed"
	RunPaused        = "paused"
)

A run's outcome. It is empty only while the run is in flight.

View Source
const (
	PolicyDetailMissed      = "the server was not running during this window"
	PolicyDetailBusy        = "another deployment occupied the window"
	PolicyDetailCreatorLost = "the policy's creator no longer holds application.deploy"
	PolicyDetailRestarted   = "the server restarted during the run"
	PolicyReasonFailures    = "three consecutive windows failed"
)

The scheduler's own sentences: the only prose a run's detail or a pause reason holds.

View Source
const (
	AuditPolicyRun    = "application.policy.run"
	AuditPolicyPaused = "application.policy.paused"
)

The scheduler's audit actions. They are not permissions: the scheduler writes them as "system", and each run's own steps are audited under the policy's creator.

View Source
const (
	SampleRetention    = 6 * time.Hour
	MaxSamplesPerFrame = protocol.MaxSamples
	// SampleCadence is the documented 60 s reporting interval; a container gains at most one
	// row per cadence (with a little slack for jitter), so the six-hour window holds about
	// 360 rows per container whatever an agent sends.
	SampleCadence  = 50 * time.Second
	EventRetention = 7 * 24 * time.Hour
	// RollupRetention keeps an hour's summary long after its samples are gone, so a week of
	// history costs 168 rows per container instead of the 10,080 the raw cadence would.
	RollupRetention = 7 * 24 * time.Hour
	// DegradedRollupRetention is the week cut to a day while the disk budget is under
	// pressure, so summaries give space back as the raw window does rather than being the one
	// telemetry pruning cannot reclaim.
	DegradedRollupRetention = 24 * time.Hour
	// DeploymentHistoryRetention keeps settled deployments; the latest succeeded row of an
	// instance's current and previous revision, every succeeded apply of an instance while a
	// rollback of it is undecided, and every row of a removed application (its own retention
	// prunes them), stay past it.
	DeploymentHistoryRetention = 90 * 24 * time.Hour
	// ApplicationRemovedRetention keeps a removed application's revisions and history.
	ApplicationRemovedRetention = 90 * 24 * time.Hour
	PruneBatch                  = 5000
)

Retention limits from docs/retention-policy.md (proposed, soak-gated).

View Source
const (
	ValidationGrace  = 30 * time.Second
	ValidationWindow = 2 * time.Minute
	ValidationPoll   = 20 * time.Second
	// PendingValidationsPerOrg is an organization's share of one tick: its oldest rows; the rest
	// wait for the next tick, so one tenant's backlog never crowds out another's.
	PendingValidationsPerOrg = 2
	// MaxPendingValidations bounds one tick's work after the per-organization cut, oldest first, so
	// it bounds organizations (MaxPendingValidations / PendingValidationsPerOrg), not one tenant's
	// backlog, and past that the organizations served rotate as their oldest rows finish.
	MaxPendingValidations = 256
	// AuditValidation is the loop's audit action, written as "system".
	AuditValidation = "application.validation"
)

Health validation: every succeeded apply is watched for a bounded time and given a verdict; an automated one that fails is rolled back when it can be (rollback.go). The loop is api.Server.RunValidations. See docs/application-schema.md, Health validation.

View Source
const (
	PhaseGrace     = "grace"
	PhaseObserving = "observing"
	PhaseDone      = "done"
)
View Source
const (
	VerdictHealthy      = "healthy"
	VerdictUnhealthy    = "unhealthy"
	VerdictExited       = "exited"
	VerdictRestarting   = "restarting"
	VerdictUnverifiable = "unverifiable"
	VerdictChanged      = "changed"
)
View Source
const (
	RollbackApplied    = "applied"
	RollbackIneligible = "ineligible"
	RollbackFailed     = "failed"

	// A rollback returns each service to its plan's Replaces image; a service that replaced no
	// container has none.
	RollbackNoPriorIdentity        = "no_prior_identity"
	RollbackPriorDefinitionInvalid = "prior_definition_invalid"
	RollbackServiceSetChanged      = "service_set_changed"
	RollbackPriorImagesMissing     = "prior_images_missing"
	RollbackInFlight               = "rollback_in_flight"
	RollbackAlreadyRolledBack      = "already_rolled_back"
	RollbackCreatorLost            = "creator_lost"
	RollbackNotSent                = "not_sent"
	RollbackInterrupted            = "interrupted"
	// A cluster rollback's service_set_changed names what changed beside it: the namespace, or
	// the claims or their mounts.
	RollbackNamespaceChanged = "namespace_changed"
	RollbackClaimsChanged    = "claims_changed"
)

A rollback's outcome, and the codes its detail holds besides plan blockers and the scheduler's codes (policyErrorCodes in internal/api).

View Source
const (
	ValidationDetailUnobserved = "the host could not be observed"
	ValidationDetailServerDown = "the server was not running during the window"
	ValidationDetailNoHealth   = "the agent cannot report container health"
	ValidationDetailNoInspect  = "the cluster agent cannot report workload status; upgrade the agent image"
	ValidationDetailInvalid    = "an inspection failed validation"
	ValidationDetailReleased   = "the application was released"

	ValidationReasonRolledBack    = "rolled back after a failed update"
	ValidationReasonNotRolledBack = "update failed and could not be rolled back: "
	ValidationReasonUnverified    = "update could not be validated: "
)

The loop's own sentences: the only prose a validation detail or its pause reason holds. A failing verdict's detail is the deciding service's name.

View Source
const (
	PresencePresent  = "present"
	PresenceGone     = "gone"
	PresenceReplaced = "replaced"
	PresenceUnknown  = "unknown" // no inventory received since the settle: inspect and wait
)

Where a settled container stands against the instance's resources and the latest inventory.

View Source
const CommandDeadline = 30 * time.Second

CommandDeadline is how long an agent has to answer before the outcome is timed out.

View Source
const CommandRetention = 7 * 24 * time.Hour

CommandRetention keeps a settled command long enough to answer "what happened to my restart" the next morning, and no longer. An unsettled one is never pruned: a command whose outcome nobody knows is exactly the record an operator needs.

View Source
const DegradedSampleRetention = time.Hour

Prune deletes expired samples and acknowledged events in bounded batches and reports how many rows went, so the caller loops until a pass removes nothing. DegradedSampleRetention is how far back raw samples are kept once the budget is under pressure: refusing new writes alone does not give space back, so the window closes too.

View Source
const DeploymentApplyDeadline = 10 * time.Minute
View Source
const DeploymentPlanTTL = 10 * time.Minute
View Source
const DirectoryEventWindow = 5 * time.Minute

DirectoryEventWindow is how far a directory event's timestamp may be from now.

View Source
const GenerationSkew = time.Hour

GenerationSkew is how far ahead of the server clock a generation (a Unix timestamp by construction) may run; anything beyond is a broken or hostile clock and is refused so it cannot pin the endpoint's inventory forever.

View Source
const InstallerConfigKey = "installer.config"
View Source
const InstallerProvider = "kyidentity"
View Source
const LocalDockerEndpointID = "ep_local_docker"
View Source
const MaxApplicationValuesBytes = 64 * 1024
View Source
const MaxDeployNamespaces = 32

MaxDeployNamespaces bounds the namespaces one cluster manifest grants writes in.

View Source
const MaxDeploymentPlanBytes = 64 * 1024
View Source
const MaxDeploymentResultStoredBytes = 160 * 1024
View Source
const MaxSampleRowsPerEndpoint = 100000

MaxSampleRowsPerEndpoint is the backstop against container-ID cardinality: 100 containers × 360 rows at the capacity targets, with headroom.

View Source
const MaxSnapshotBytes = protocol.MaxSnapshotBytes

MaxSnapshotBytes is the shared limit from the protocol package.

Variables

View Source
var (
	ErrKeyRetired       = errors.New("key retired")
	ErrKeyPendingReview = errors.New("key pending review")
)

Sentinel errors let the handshake tell a retired key (the agent should switch to its acknowledged one) from a key still under review (the agent must keep using its old one).

View Source
var (
	ErrMigrationOpen        = errors.New("the application has an open migration")
	ErrMigrationNotReady    = errors.New("the migration report is not ready")
	ErrMigrationState       = errors.New("the migration's status does not allow this")
	ErrMigrationStale       = errors.New("the source definition changed since the analysis")
	ErrStorageClassUnknown  = errors.New("the destination reports no such StorageClass")
	ErrSizeInvalid          = errors.New("invalid claim size")
	ErrVolumeUnknown        = errors.New("the source mounts no such named volume")
	ErrApplicationNameTaken = errors.New("the destination's name is taken")
	// ErrDestinationNameTooLong is a destination name over validTenantName's 255 bytes.
	ErrDestinationNameTooLong = errors.New("the destination's name would be too long")
	// ErrInventoryStale is a destination cluster whose inventory is not fresh (freshInventory).
	ErrInventoryStale = errors.New("the destination cluster's inventory is stale")
)
View Source
var (
	ErrCommandInProgress = errors.New("a container recreate or run, or a workload apply or run, is in flight on this endpoint")
	ErrContainerManaged  = errors.New("the container belongs to an adopted application")
)
View Source
var (
	// ErrNamespaceNotGranted is a cluster write in a namespace the endpoint's manifest does not
	// list: refused before any frame, whatever the agent's grant says.
	ErrNamespaceNotGranted = errors.New("the cluster's manifest does not grant writes in that namespace")
	// ErrWorkloadManaged is a configuration read, apply or delete of a workload a KyYard
	// application deployed; it changes through its application.
	ErrWorkloadManaged = errors.New("the workload belongs to a KyYard application")
)
View Source
var (
	ErrInvalidTimezone = errors.New("invalid update policy time zone")
	ErrInvalidWindow   = errors.New("invalid update policy window")
	ErrInvalidWeekdays = errors.New("invalid update policy weekdays")
	ErrInvalidMode     = errors.New("invalid update policy mode")
	ErrPolicyNotPaused = errors.New("update policy is not paused")
	// ErrPolicyChanged: a policy run's policy was deleted, paused, switched to plan_only or
	// saved by someone else before its apply.
	ErrPolicyChanged = errors.New("update policy changed during its run")
)
View Source
var (
	ErrRevisionCorrupt  = errors.New("application revision digest mismatch")
	ErrRevisionConflict = errors.New("application revision changed")
	ErrApplicationLimit = errors.New("application storage limit reached")
	ErrForbidden        = errors.New("tenant access denied")
	ErrInvalid          = errors.New("invalid tenant input")
	ErrNotFound         = errors.New("record not found")
	ErrAlreadyExists    = errors.New("record already exists")
	ErrLastAdmin        = errors.New("organization needs one active administrator")
	ErrStaleEvent       = errors.New("directory event already applied or older than the last one")
	// ErrEndpointOffline says the endpoint is not connected, so there is nowhere to send a
	// command. It is distinct from a bad request: the caller asked for something reasonable
	// that cannot happen right now.
	ErrEndpointOffline   = errors.New("endpoint is not connected")
	ErrInUse             = errors.New("record is still referenced")
	ErrRotationPending   = errors.New("a rotated key is already awaiting review")
	ErrRotationBlocked   = errors.New("rotation is blocked until a duplicate connection is cleared")
	ErrSessionExpired    = errors.New("session expired")
	ErrPairingExpired    = errors.New("pairing session expired")
	ErrDeploymentPlanned = errors.New("a live deployment plan exists")
	// ErrDeploymentInProgress says a deployment is applying: neither a new plan nor a
	// release may proceed until it settles.
	ErrDeploymentInProgress = errors.New("a deployment is being applied")
	// ErrRemovalTooLarge says an instance holds more containers than one removal frame names.
	ErrRemovalTooLarge = errors.New("instance has more containers than one removal takes")
	// ErrRegistryNotConfigured says an image's host has no registry entry and the
	// organization has not opted in to anonymous pulls.
	ErrRegistryNotConfigured = errors.New("registry not configured")
	// ErrPrivateRegistriesDisabled refuses allow_private while the operator has not set
	// KY_REGISTRY_ALLOW_PRIVATE.
	ErrPrivateRegistriesDisabled = errors.New("private registries are disabled by the operator")
	ErrMappingRequired           = errors.New("application has no adopted mapping")
	// ErrRuntimeUnsupported is an action the endpoint's runtime cannot take: a Docker action on
	// a Kubernetes endpoint, a pod log on a Docker one.
	ErrRuntimeUnsupported = errors.New("the endpoint's runtime does not support this action")
	// ErrUnreadableResult is a deployment result that fails validation even read as an older
	// agent's: nothing is stored, and the row waits for another answer or the sweep.
	ErrUnreadableResult = errors.New("unreadable deployment result")
	// ErrNamespaceUnknown is a mapping to a namespace the cluster's manifest does not list.
	ErrNamespaceUnknown = errors.New("the namespace is not one the cluster's manifest grants")
	// ErrPairingCodesExhausted says every draw within the retry budget collided with another
	// organization's still-live code; the caller may simply try again.
	ErrPairingCodesExhausted = errors.New("could not mint a unique pairing code")
)
View Source
var ErrAdoptionChanged = errors.New("adoption preview changed or inventory unavailable")
View Source
var ErrApplicationAdopted = errors.New("application has an adopted instance")
View Source
var ErrInstallerOwned = errors.New("installer-owned authority")
View Source
var ErrSampleBudget = errors.New("endpoint sample budget exhausted")
View Source
var ImageCheckDeadline = 60 * time.Second

ImageCheckDeadline bounds every registry call of one check; the API sizes its write deadline from it. A var so a test can shorten it.

View Source
var MigrationAcknowledgeable = []string{"network_references", "port_unpublished", "healthcheck_dropped", "resource_limits_dropped", "read_only_rootfs"}

MigrationAcknowledgeable are the analyzer's codes an operator answers by acknowledging them: in an application of several services, the names the others must use and a service no other can reach; and the settings the destination drops (a healthcheck, resource limits, a read-only root). The analyzer owns the vocabulary; the store only bounds the request.

Functions

func BaselineOf

func BaselineOf(obs []Observation) (map[string]ServiceBaseline, bool)

BaselineOf is the baseline a first observation gives, false unless every service was either inspected or is known gone or replaced. A cluster service whose Deployment is already missing, recreated or edited gives none: its next poll judges it changed. One listing no pods gets an empty PodRestarts: the first pods to appear are the rollout's own, and their restarts count from 0.

func CheckMigrationChoices

func CheckMigrationChoices(spec ApplicationSpec, classes []protocol.StorageClass, choices MigrationChoices) error

CheckMigrationChoices holds choices to at most MaxKubernetesClaims volumes and distinct MigrationAcknowledgeable codes, each volume to a named volume of spec, a valid size, ReadWriteOnce and one of the destination's classes. The API checks before it spends an inspection; the store checks again against the head revision.

func CleanAbsolutePath

func CleanAbsolutePath(p string) bool

CleanAbsolutePath holds mount paths to absolute, normalized, display-safe (so at most 255 bytes) text with no surrounding whitespace.

func CommandPermission

func CommandPermission(action string) (permissions.Action, bool)

CommandPermission is the permission a command action needs; false for an unknown action.

func ExecPodResource

func ExecPodResource(endpointID string, pod protocol.PodTarget) string

ExecPodResource is the audit resource of a pod terminal: endpoint/pods/namespace/pod/container.

func InstallerUserID

func InstallerUserID(issuer, subject string) string

func Judge

func Judge(obs []Observation, baseline map[string]ServiceBaseline, final bool) (verdict, detail string)

Judge applies the verdict rules to one poll against baseline. It returns the terminal verdict and the service that decided it, or "" while the window goes on. final: observe_until has passed, so a service still starting is unhealthy and a complete, passing poll is healthy.

func KubernetesProject

func KubernetesProject(name string) string

KubernetesProject is the project a Kubernetes instance takes from its application's name: lower-case letters and digits, other runs as one '-', at most 63 characters, "app" when nothing is left. It names the instance's objects and labels.

func NormalizeNamespaces

func NormalizeNamespaces(in []string) ([]string, error)

NormalizeNamespaces sorts a namespace list and refuses one with more than MaxDeployNamespaces entries, a repeat, a name that is not a DNS-1123 label, the agent's own namespace (its Secrets are its identity) or a kube- system namespace.

func NormalizeRegistryHost

func NormalizeRegistryHost(host string) (string, error)

NormalizeRegistryHost returns host as image references name it: lowercase host[:port], with Docker Hub's aliases folded into docker.io. Like ParseReference, it takes only localhost or a name with a dot or port as a registry.

func ParseStoredTime

func ParseStoredTime(v any) (time.Time, error)

ParseStoredTime reads a timestamp in whichever shape the driver returned it. It is exported because anything inspecting the database from outside has to agree with the store about what it wrote, and a second, narrower copy of these layouts would drift silently.

func ValidDeclaredVolumeName

func ValidDeclaredVolumeName(name string) bool

ValidDeclaredVolumeName is the grammar for a newly declared volume name, at least two characters. Importers use this, not ValidVolumeName, so a one-character name can no longer be declared even though one already stored still validates.

func ValidEndpointName

func ValidEndpointName(name string) bool

ValidEndpointName is the rule Enroll and RenameEndpoint apply to an endpoint's name.

func ValidVolumeName

func ValidVolumeName(name string) bool

ValidVolumeName is Docker's volume name grammar, as stored: it also validates volumes in revisions saved before ValidDeclaredVolumeName's two-character minimum took effect.

func ValidateApplicationSpec

func ValidateApplicationSpec(spec ApplicationSpec) error

func ValidateInstallerBundle

func ValidateInstallerBundle(b applysetup.Common) error

func ValidateSamples

func ValidateSamples(m protocol.Metrics) error

func VolumeHostName

func VolumeHostName(project string, v DeclaredVolume) string

VolumeHostName is the runtime volume name, as docker compose derives it.

func WorkloadAction

func WorkloadAction(action string) bool

WorkloadAction reports whether action targets a Kubernetes workload or pod.

Types

type AdoptedContainer

type AdoptedContainer struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	ImageID   string    `json:"image_id"`
	CreatedAt time.Time `json:"created_at"`
	// Mounts as the inventory reported them; nil when the agent reports none (older than
	// mounts). Preflight compares binds against them.
	Mounts          []protocol.Mount `json:"mounts,omitempty"`
	MountsTruncated bool             `json:"mounts_truncated,omitempty"`
}

type AdoptionPreview

type AdoptionPreview struct {
	ApplicationID   string             `json:"application_id"`
	ApplicationName string             `json:"application_name"`
	Revision        int                `json:"revision"`
	EndpointID      string             `json:"endpoint_id"`
	EndpointName    string             `json:"endpoint_name"`
	Project         string             `json:"project"`
	Containers      []AdoptedContainer `json:"containers"`
	Digest          string             `json:"digest"`
}

type AdoptionRequest

type AdoptionRequest struct {
	EndpointID string `json:"endpoint_id"`
	Project    string `json:"project"`
	Digest     string `json:"digest"`
	Confirm    string `json:"confirm"`
}

type AgentIdentity

type AgentIdentity struct {
	Endpoint    Endpoint
	PublicKey   []byte
	Fingerprint string
}

AgentIdentity is what the connection handshake needs: the endpoint and the key that may authenticate it. Pending endpoints authenticate with their key under review so they can receive the approval notice; nothing else is granted to them.

type Application

type Application struct {
	ID             string    `json:"id"`
	OrganizationID string    `json:"organization_id"`
	EnvironmentID  string    `json:"environment_id"`
	Name           string    `json:"name"`
	LatestRevision int       `json:"latest_revision"`
	CreatedBy      string    `json:"created_by"`
	CreatedAt      time.Time `json:"created_at"`
	// RemovedAt is set once a removal took its containers off the host; revisions and history
	// stay until discard or ApplicationRemovedRetention.
	RemovedAt *time.Time `json:"removed_at"`
}

type ApplicationComparison

type ApplicationComparison struct {
	InstanceID      string                `json:"instance_id"`
	EndpointID      string                `json:"endpoint_id"`
	EndpointName    string                `json:"endpoint_name"`
	Project         string                `json:"project"`
	Revision        int                   `json:"revision"`
	AdoptedRevision int                   `json:"adopted_revision"`
	Digest          string                `json:"digest"`
	ReceivedAt      *time.Time            `json:"received_at"`
	Availability    string                `json:"availability"`
	Services        []ComparisonService   `json:"services"`
	Containers      []ComparisonContainer `json:"containers"`
}

type ApplicationInstance

type ApplicationInstance struct {
	ContainerCount   int                `json:"container_count"`
	EndpointName     string             `json:"endpoint_name"`
	ID               string             `json:"id"`
	ApplicationID    string             `json:"application_id"`
	EndpointID       string             `json:"endpoint_id"`
	Project          string             `json:"project"`
	Revision         int                `json:"revision"`
	MappingVersion   int                `json:"mapping_version"`
	CurrentRevision  int                `json:"current_revision"`
	PreviousRevision int                `json:"previous_revision"`
	CreatedBy        string             `json:"created_by"`
	CreatedAt        time.Time          `json:"created_at"`
	Containers       []AdoptedContainer `json:"containers"`
	// Namespace is set exactly for an instance mapped to a Kubernetes endpoint.
	Namespace string `json:"namespace,omitempty"`
}

type ApplicationMapping

type ApplicationMapping struct {
	InstanceID     string            `json:"instance_id"`
	Version        int               `json:"version"`
	MappedRevision int               `json:"mapped_revision"`
	Preview        *AdoptionPreview  `json:"preview"`
	Services       []string          `json:"services"`
	Bindings       map[string]string `json:"bindings"`
	// Runtime is the endpoint's. A Kubernetes mapping names its Namespace and the namespaces
	// the cluster's manifest grants; it has no containers and no bindings.
	Runtime          string   `json:"runtime"`
	Namespace        string   `json:"namespace,omitempty"`
	DeployNamespaces []string `json:"deploy_namespaces,omitempty"`
}

type ApplicationMigration

type ApplicationMigration struct {
	ID                         string           `json:"id"`
	ApplicationID              string           `json:"application_id"`
	ApplicationName            string           `json:"application_name"`
	DestinationApplicationID   string           `json:"destination_application_id,omitempty"`
	DestinationApplicationName string           `json:"destination_application_name,omitempty"`
	DestinationEndpointID      string           `json:"destination_endpoint_id"`
	Namespace                  string           `json:"namespace"`
	Status                     string           `json:"status"`
	SourceRevision             int              `json:"source_revision"`
	Ready                      bool             `json:"ready"`
	Report                     json.RawMessage  `json:"report"`
	Choices                    MigrationChoices `json:"choices"`
	CreatedBy                  string           `json:"created_by"`
	CreatedAt                  time.Time        `json:"created_at"`
	UpdatedAt                  time.Time        `json:"updated_at"`
	ValidatedBy                string           `json:"validated_by,omitempty"`
	ValidatedAt                *time.Time       `json:"validated_at,omitempty"`
	ValidatedNote              string           `json:"validated_note,omitempty"`
	ConfirmedBy                string           `json:"confirmed_by,omitempty"`
	ConfirmedAt                *time.Time       `json:"confirmed_at,omitempty"`
	CutoverNote                string           `json:"cutover_note,omitempty"`
	Role                       string           `json:"role"`
}

ApplicationMigration is one migration of a Docker source to a destination application on a cluster. Report is the analyzer's JSON, stored as produced. Role says which end the application it was read for is.

type ApplicationPort

type ApplicationPort struct {
	Target    int    `json:"target"`
	Published int    `json:"published"`
	HostIP    string `json:"host_ip,omitempty"`
	Protocol  string `json:"protocol"`
}

type ApplicationRevision

type ApplicationRevision struct {
	ID             string          `json:"id"`
	ApplicationID  string          `json:"application_id"`
	OrganizationID string          `json:"organization_id"`
	EnvironmentID  string          `json:"environment_id"`
	Number         int             `json:"number"`
	Spec           ApplicationSpec `json:"spec"`
	Digest         string          `json:"digest"`
	CreatedBy      string          `json:"created_by"`
	CreatedAt      time.Time       `json:"created_at"`
}

type ApplicationSecretRef

type ApplicationSecretRef struct {
	SecretRef string `json:"secret_ref"`
}

type ApplicationService

type ApplicationService struct {
	Ports       []ApplicationPort               `json:"ports,omitempty"`
	Restart     string                          `json:"restart,omitempty"`
	Name        string                          `json:"name"`
	Image       string                          `json:"image"`
	Environment map[string]ApplicationSecretRef `json:"environment,omitempty"`
	Volumes     []ApplicationVolume             `json:"volumes,omitempty"`
}

type ApplicationSpec

type ApplicationSpec struct {
	Kind     string               `json:"kind"`
	Services []ApplicationService `json:"services"`
	Volumes  []DeclaredVolume     `json:"volumes,omitempty"`
	// Kubernetes holds what only a cluster needs: a migration's destination revision carries
	// storage and networking choices. The Compose importer never sets it.
	Kubernetes *KubernetesExtension `json:"kubernetes,omitempty"`
}

ApplicationSpec is the initial secret-reference-only desired-state contract. It is not a Compose document or a reconstruction of runtime inventory. Import must reject unsupported fields before constructing it (docs/application-schema.md).

type ApplicationVolume

type ApplicationVolume struct {
	Kind     string `json:"kind"`
	Source   string `json:"source"`
	Target   string `json:"target"`
	ReadOnly bool   `json:"read_only,omitempty"`
}

ApplicationVolume mounts a declared volume (Kind "named", Source its name) or an absolute host path (Kind "bind") at Target.

type AuditRecord

type AuditRecord struct {
	Scope          string    `json:"scope"`
	OrganizationID string    `json:"organization_id"`
	EnvironmentID  string    `json:"environment_id"`
	CorrelationID  string    `json:"correlation_id"`
	Result         string    `json:"result"`
	ID             int64     `json:"id"`
	UserID         string    `json:"user_id"`
	Action         string    `json:"action"` // e.g. "auth.login", "scim.user_created"
	Resource       string    `json:"resource"`
	Details        string    `json:"details,omitempty"`
	IPAddress      string    `json:"ip_address"`
	CreatedAt      time.Time `json:"created_at"`
}

AuditRecord records actor (UserID), target (Resource), scope and outcome.

type AuditStore

type AuditStore interface {
	LogAudit(ctx context.Context, r *AuditRecord) error
	ListAuditRecords(ctx context.Context, offset, limit int) ([]*AuditRecord, int, error)
}

AuditStore logs security events.

type BlockedService

type BlockedService struct {
	Name        string            `json:"name"`
	Blockers    []string          `json:"blockers"`
	Unsupported []string          `json:"unsupported,omitempty"`
	Details     map[string]string `json:"details,omitempty"`
}

BlockedService names a service a refused plan blocked on: its own blockers and the codes a live inspection reported, nothing else about it.

type Command

type Command struct {
	ID             string               `json:"id"`
	EndpointID     string               `json:"endpoint_id"`
	OrganizationID string               `json:"organization_id"`
	EnvironmentID  string               `json:"environment_id"`
	ActorID        string               `json:"actor_id"`
	RequestID      string               `json:"request_id"`
	Action         string               `json:"action"`
	ContainerID    string               `json:"container_id,omitempty"`
	Reference      string               `json:"reference,omitempty"`
	Expects        protocol.Expectation `json:"expects"`
	Deadline       time.Time            `json:"deadline"`
	Outcome        string               `json:"outcome"`
	Detail         string               `json:"detail,omitempty"`
	CreatedAt      time.Time            `json:"created_at"`
	DispatchedAt   *time.Time           `json:"dispatched_at,omitempty"`
	SettledAt      *time.Time           `json:"settled_at,omitempty"`
	// Result is a direct recreate or run's step table; nil for every other command.
	Result *storedDeploymentResult `json:"result,omitempty"`
	// ResultContainerID is the container a direct command created; ContainerID stays its target.
	ResultContainerID string `json:"result_container_id,omitempty"`
}

Command is one instruction and what became of it. Outcome is empty while the command is in flight; afterwards it is one of protocol's five, and the set is closed, so a command whose answer never arrived reads as unknown rather than as a row with nothing in it.

func (*Command) InFlight

func (c *Command) InFlight() bool

InFlight reports whether the command is still waiting for an answer.

type ComparisonContainer

type ComparisonContainer struct {
	ID              string `json:"id"`
	Name            string `json:"name"`
	Ownership       string `json:"ownership"`
	Service         string `json:"service"`
	Image           string `json:"image"`
	ImageComparison string `json:"image_comparison"`
	State           string `json:"state"`
}

type ComparisonService

type ComparisonService struct {
	Name     string `json:"name"`
	Image    string `json:"image"`
	Observed int    `json:"observed"`
}

type DeclaredVolume

type DeclaredVolume struct {
	Name     string `json:"name"`
	External bool   `json:"external,omitempty"`
}

type Deployment

type Deployment struct {
	ID             string         `json:"id"`
	ApplicationID  string         `json:"application_id"`
	InstanceID     string         `json:"instance_id"`
	EndpointID     string         `json:"endpoint_id"`
	EndpointName   string         `json:"endpoint_name"`
	Kind           string         `json:"kind"`
	State          string         `json:"state"`
	Revision       int            `json:"revision"`
	SpecDigest     string         `json:"spec_digest"`
	MappingVersion int            `json:"mapping_version"`
	Plan           DeploymentPlan `json:"plan"`
	CreatedBy      string         `json:"created_by"`
	CreatedAt      time.Time      `json:"created_at"`
	ExpiresAt      time.Time      `json:"expires_at"`
	Expired        bool           `json:"expired"`
	AppliedBy      string         `json:"applied_by"`
	AppliedAt      *time.Time     `json:"applied_at"`
	Deadline       *time.Time     `json:"deadline"`
	SettledAt      *time.Time     `json:"settled_at"`
	Detail         string         `json:"detail"`
	// CorrelationID is the plan's request ID: the frame and every audit row of the deployment carry it.
	CorrelationID string                     `json:"correlation_id"`
	Result        *protocol.DeploymentResult `json:"result"` // nil until settled
	// Validation is the deployment's health validation: nil for a plan, a removal or an apply
	// that did not succeed.
	Validation *Validation `json:"validation,omitempty"`
	// MigrationID names the open migration whose destination this apply deployed, set when the
	// row turned applying.
	MigrationID string `json:"migration_id,omitempty"`
	// RetainedClaims names the claims a settled cluster removal reported kept (retainedClaims).
	RetainedClaims []string `json:"retained_claims,omitempty"`
}

type DeploymentPlan

type DeploymentPlan struct {
	Project    string              `json:"project"`
	Services   []PlannedService    `json:"services"`
	Containers []RemovalPlanTarget `json:"containers,omitempty"`
	// Volumes are the named volumes the agent ensures, host names in first-use order.
	Volumes []string `json:"volumes,omitempty"`
	// Namespace is set exactly for a Kubernetes plan or removal.
	Namespace string `json:"namespace,omitempty"`
	// Claims are the PersistentVolumeClaims a Kubernetes apply ensures.
	Claims []protocol.KubernetesClaim `json:"claims,omitempty"`
}

DeploymentPlan is what a row decided: the services an apply replaces, or the containers a removal stops and deletes (Services then empty).

type DeploymentPreflight

type DeploymentPreflight struct {
	InstanceID     string             `json:"instance_id"`
	EndpointID     string             `json:"endpoint_id"`
	EndpointName   string             `json:"endpoint_name"`
	Revision       int                `json:"revision"`
	MappingVersion int                `json:"mapping_version"`
	ReceivedAt     time.Time          `json:"received_at"`
	Executable     bool               `json:"executable"`
	Blockers       []string           `json:"blockers"`
	Services       []PreflightService `json:"services"`
}

type DevicePairing

type DevicePairing struct {
	// Secret and PushToken never serialise: this record is reached by unauthenticated
	// pair/verify and pair/poll callers. A handler that must return one needs its own type.
	Secret     string    `json:"-"` // Ephemeral secret for exchange
	UserID     string    `json:"user_id,omitempty"`
	DeviceName string    `json:"device_name,omitempty"`
	Platform   string    `json:"platform,omitempty"` // "android", "ios", "pwa", "desktop"
	PushToken  string    `json:"-"`
	Status     string    `json:"status"` // "pending", "approved", "consumed", "expired"
	CreatedAt  time.Time `json:"created_at"`
	ExpiresAt  time.Time `json:"expires_at"`
}

DevicePairing represents a 90-second ephemeral session to link mobile/PWA wrappers.

type DeviceStore

type DeviceStore interface {
	CreatePairing(ctx context.Context, p *DevicePairing) error
	GetPairingBySecret(ctx context.Context, secret string) (*DevicePairing, error)
	ConsumePairing(ctx context.Context, secret, deviceName, platform, pushToken string) error
	CleanExpiredPairings(ctx context.Context) error
}

DeviceStore handles 90s ephemeral QR pairing sessions and paired push clients.

type DigestResolver

type DigestResolver interface {
	Head(ctx context.Context, ref registry.Reference, cred *registry.Credential, allowPrivate bool) (string, error)
	// CheckHost refuses ref's host where Head would, without a registry request.
	CheckHost(ctx context.Context, ref registry.Reference, allowPrivate bool) error
}

DigestResolver is what the check needs from a registry client. The API satisfies it with internal/registry; tests use a fake. Head must return once ctx ends: the check waits for it.

type DirectCommand

type DirectCommand struct {
	Action string
	// Confirm repeats the container's current name (recreate) or the new one (run).
	Confirm string
	// State is the replaced container's state the operator saw; recreate only.
	State         string
	MaxFrameBytes int
	// AllowPrivileged is KY_CONTAINER_ALLOW_PRIVILEGED: without it a host-level setting is refused.
	AllowPrivileged bool
	// Frame has one explicit service; identity, issue time and deadline are set here.
	Frame protocol.DeploymentRequest
}

DirectCommand is a recreate or run as the API built it.

type DirectoryEvent

type DirectoryEvent struct {
	Provider, ID, Subject string
	At                    time.Time
}

DirectoryEvent identifies one signed identity provider event.

type DirectoryTx

type DirectoryTx interface {
	GetUserBySSO(provider, subject string) (*User, error)
	GetUserByUsername(username string) (*User, error)
	CreateUser(u *User) error
	// UpdateUserIdentity writes only username, email, display name, role and status, leaving
	// credential and MFA columns to their own compare-and-swap writers.
	UpdateUserIdentity(u *User) error
	DeleteUser(id string) error
	DeleteUserSessions(userID string) error
}

DirectoryTx is the identity provider's reads and writes inside one event's transaction. Its writes always apply; an organization they leave without an active administrator gets an organization.last_admin_lost audit row.

type Endpoint

type Endpoint struct {
	ID             string            `json:"id"`
	OrganizationID string            `json:"organization_id"`
	EnvironmentID  string            `json:"environment_id"`
	Name           string            `json:"name"`
	Runtime        string            `json:"runtime"`
	State          string            `json:"state"`
	Facts          map[string]string `json:"facts"`
	Fingerprint    string            `json:"fingerprint"` // key under review while pending, approved key afterwards
	// PendingFingerprint is a rotated key awaiting operator acknowledgement, if any.
	PendingFingerprint string          `json:"pending_fingerprint,omitempty"`
	Capabilities       []string        `json:"capabilities"`
	Alerts             []EndpointEvent `json:"alerts"` // unacknowledged high-severity events
	// ClusterHealth is derived on read from the stored inventory; Kubernetes endpoints only.
	ClusterHealth string     `json:"cluster_health,omitempty"`
	CreatedAt     time.Time  `json:"created_at"`
	ApprovedAt    *time.Time `json:"approved_at,omitempty"`
	ApprovedBy    string     `json:"approved_by,omitempty"`
	RevokedAt     *time.Time `json:"revoked_at,omitempty"`
	LastSeenAt    *time.Time `json:"last_seen_at,omitempty"`
	// DeployNamespaces are the namespaces the cluster's last manifest grants writes in, sorted;
	// empty for a Docker endpoint.
	DeployNamespaces []string `json:"deploy_namespaces"`
}

Endpoint is an enrolled (or enrolling) host. Facts are the bounded enrollment report.

type EndpointEvent

type EndpointEvent struct {
	ID             int64      `json:"id"`
	Severity       string     `json:"severity"`
	Kind           string     `json:"kind"`
	Details        string     `json:"details"`
	CreatedAt      time.Time  `json:"created_at"`
	AcknowledgedAt *time.Time `json:"acknowledged_at,omitempty"`
}

EndpointEvent is a bounded, operator-facing record; high-severity ones surface until acknowledged.

type EnrollmentRequest

type EnrollmentRequest struct {
	Token     []byte
	PublicKey []byte
	Proof     []byte
	Name      string
	Facts     map[string]string
	IPAddress string
}

EnrollmentRequest is what an agent presents; everything in it is untrusted.

type EnrollmentToken

type EnrollmentToken struct {
	ID            string    `json:"id"`
	EnvironmentID string    `json:"environment_id"`
	Runtime       string    `json:"runtime"`
	ExpiresAt     time.Time `json:"expires_at"`
	AgentImage    string    `json:"agent_image,omitempty"`
	Secret        []byte    `json:"-"`
}

EnrollmentToken is returned once; Secret is the raw token and is never stored.

type Environment

type Environment struct {
	ID             string `json:"id"`
	OrganizationID string `json:"organization_id"`
	Name           string `json:"name"`
}

type ImageCheck

type ImageCheck struct {
	Service      string    `json:"service"`
	Reference    string    `json:"reference"`
	LocalDigest  string    `json:"local_digest"`
	RemoteDigest string    `json:"remote_digest"`
	Verdict      string    `json:"verdict"`
	Detail       string    `json:"detail"`
	CheckedAt    time.Time `json:"checked_at"`
}

ImageCheck is the cached update verdict for one mapped service.

type InvalidSpecError

type InvalidSpecError struct{ Blockers []string }

InvalidSpecError names what stops a direct command's configuration.

func (*InvalidSpecError) Error

func (e *InvalidSpecError) Error() string

type Inventory

type Inventory struct {
	EndpointID string          `json:"endpoint_id"`
	State      string          `json:"state"`
	Generation uint64          `json:"generation"`
	ObservedAt time.Time       `json:"observed_at"`
	ReceivedAt time.Time       `json:"received_at"`
	Snapshot   json.RawMessage `json:"snapshot"`
}

Inventory is the stored snapshot plus the freshness facts the UI needs to tell stale from current: when the agent observed it, when the server received it, and the endpoint state.

type KubernetesExtension

type KubernetesExtension struct {
	Volumes    map[string]KubernetesVolume `json:"volumes"`
	ServiceIPs map[string]string           `json:"service_ips,omitempty"`
}

KubernetesExtension carries storage choices per named volume and optional Service IPs. A chosen volume becomes a PersistentVolumeClaim on a cluster.

type KubernetesObject

type KubernetesObject struct {
	Namespace string `json:"namespace"`
	Name      string `json:"name"`
}

KubernetesObject names a service's objects: the Deployment and Service <name>, the ConfigMap <name>-env-<suffix> and the Secret <name>-secret-<suffix>.

type KubernetesVolume

type KubernetesVolume struct {
	StorageClass string `json:"storage_class"`
	Size         string `json:"size"`
	AccessMode   string `json:"access_mode"`
}

KubernetesVolume is one volume's claim: StorageClass "" is the cluster default, Size a whole number of Mi, Gi or Ti from 1Mi to 16Ti, AccessMode ReadWriteOnce.

func (KubernetesVolume) Valid

func (v KubernetesVolume) Valid() bool

Valid checks one choice's grammar; the destination inventory decides whether its class exists.

type LogTarget

type LogTarget struct {
	ContainerID string `json:"container_id"`
	Name        string `json:"name"`
}

LogTarget is the container a log request resolved to, as the endpoint last reported it.

type MFAChallenge

type MFAChallenge struct {
	TokenHash string
	UserID    string
	ExpiresAt time.Time
}

type MFAState

type MFAState struct {
	SecretEnc         string
	Enabled           bool
	RecoveryCodesHash string
}

MFAState is the second-factor slice of a users row that self-service enrolment changes.

type MappingRequest

type MappingRequest struct {
	InstanceID string            `json:"instance_id"`
	Version    int               `json:"version"`
	Digest     string            `json:"digest"`
	Confirm    string            `json:"confirm"`
	Bindings   map[string]string `json:"bindings"`
	EndpointID string            `json:"endpoint_id"`
	Namespace  string            `json:"namespace"`
}

MappingRequest binds services to adopted containers (Docker), or, with EndpointID and Namespace and nothing else, maps the application to a namespace of a Kubernetes endpoint.

type MemberOrganization

type MemberOrganization struct {
	ID   string     `json:"id"`
	Name string     `json:"name"`
	Role TenantRole `json:"role"`
}

MemberOrganization is one of the caller's own organizations with the role held there.

type MigrationAnalysis

type MigrationAnalysis struct {
	Revision int
	Report   []byte
	Ready    bool
}

MigrationAnalysis is what the analyzer produced for revision Revision of the source.

type MigrationChoices

type MigrationChoices struct {
	Volumes      map[string]KubernetesVolume `json:"volumes"`
	Acknowledged []string                    `json:"acknowledged"`
}

MigrationChoices are the operator's answers: a claim per named volume, and the findings they acknowledge that have nothing to record (MigrationAcknowledgeable).

type MigrationDestination

type MigrationDestination struct {
	EndpointID     string
	Namespaces     []string
	StorageClasses []protocol.StorageClass
	Name           string
	Project        string
}

MigrationDestination is the cluster side: its namespaces and StorageClasses, and the name and project the destination application takes.

type MigrationSource

type MigrationSource struct {
	ApplicationName string
	Revision        int
	Spec            ApplicationSpec
	Project         string
	EndpointID      string
	Containers      map[string]protocol.Container
	Volumes         []protocol.Volume
	Destination     MigrationDestination
}

MigrationSource is everything the analyzer reads: the source's latest revision, its project and mapped containers by service from the endpoint's fresh inventory, the endpoint's volumes, and the destination.

type MigrationStart

type MigrationStart struct {
	DestinationEndpointID string `json:"destination_endpoint_id"`
	Namespace             string `json:"namespace"`
}

MigrationStart names the cluster and namespace a migration targets.

type Observation

type Observation struct {
	Service    string
	Presence   string
	Inspection *protocol.ContainerInspection
	Generation int64
}

Observation is one service at one poll: its presence and, when one succeeded, its inspection. Generation is a cluster service's settled Deployment generation.

type ObservedService

type ObservedService struct {
	protocol.DeploymentIdentity
	Presence string
}

ObservedService is a settled identity and where it stands now.

type Organization

type Organization struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
}

type OrganizationGroup

type OrganizationGroup struct {
	ID             string `json:"id"`
	OrganizationID string `json:"organization_id"`
	Name           string `json:"name"`
}

type OrganizationMember

type OrganizationMember struct {
	UserID   string     `json:"user_id"`
	Username string     `json:"username"`
	Role     TenantRole `json:"role"`
	Status   string     `json:"status"`
}

OrganizationMember is a membership joined with the account it names.

type OrganizationMembership

type OrganizationMembership struct {
	OrganizationID string     `json:"organization_id"`
	UserID         string     `json:"user_id"`
	Role           TenantRole `json:"role"`
	Status         string     `json:"status"`
}

type OrganizationSummary

type OrganizationSummary struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	Members   int       `json:"members"` // active memberships
}

type PendingValidation

type PendingValidation struct {
	Validation
	OrganizationID, EnvironmentID, ApplicationID, InstanceID, EndpointID string
	Baseline                                                             map[string]ServiceBaseline
	Services                                                             []ObservedService
	// Health: the endpoint's agent can report what validation reads, container.inspect.health
	// on Docker and kubernetes.inspect on a cluster (Kubernetes).
	Health, Kubernetes bool
	// Released: the instance is gone.
	Released bool
	// PolicyID and CreatedBy name the automated run's policy; empty once it is deleted.
	PolicyID, CreatedBy string
}

PendingValidation is a validation the loop still has work on, with what it needs to do it.

type PlanRequest

type PlanRequest struct {
	InstanceID     string `json:"instance_id"`
	MappingVersion int    `json:"mapping_version"`
	Revision       int    `json:"revision"`
	Confirm        string `json:"confirm"`
	// Update names services to pull from the registry, pinned to its digest at plan time.
	Update []string `json:"update"`
	// Set by the API, never read from a client: the largest frame the endpoint's agent accepts,
	// and one live inspection per mapped container, keyed by container ID.
	MaxFrameBytes int                                     `json:"-"`
	Inspections   map[string]protocol.ContainerInspection `json:"-"`
	// PinImages is set only by a validation's rollback: service to image ID, taken instead of
	// resolving the service's tag. The ID must be on the host; nothing is pulled. On a cluster it
	// maps every service to a digest reference (host/repository@sha256:...), which the plan pulls
	// with no registry call.
	PinImages map[string]string `json:"-"`
}

type PlannedService

type PlannedService struct {
	Name        string                    `json:"name"`
	Reference   string                    `json:"reference"`
	ImageID     string                    `json:"image_id"`
	ImageDigest string                    `json:"image_digest"`
	ContainerID string                    `json:"container_id"`
	Replaces    protocol.InspectionTarget `json:"replaces"`
	Restart     string                    `json:"restart"`
	Ports       []ApplicationPort         `json:"ports"`
	SecretRefs  []string                  `json:"secret_refs"`
	// Set for a pulled service: apply sends the pull and no image ID.
	PullReference string `json:"pull_reference,omitempty"`
	PullDigest    string `json:"pull_digest,omitempty"`
	// Mounts are the preflight's resolved list; binds on it are already on the replaced container.
	Mounts []protocol.Mount `json:"mounts,omitempty"`
	// DroppedMounts are the replaced container's mounts the recreate leaves off, for approval.
	DroppedMounts []protocol.Mount `json:"dropped_mounts,omitempty"`
	// Object is the Deployment (and Service) a Kubernetes plan applies for this service; nil on
	// Docker. Replaces, ImageID and Mounts are then empty and the service always pulls.
	Object *KubernetesObject `json:"object,omitempty"`
	// ClaimMounts mount the plan's claims into a Kubernetes service.
	ClaimMounts []protocol.KubernetesMount `json:"claim_mounts,omitempty"`
	ClusterIP   string                     `json:"cluster_ip,omitempty"`
}

type PolicyInput

type PolicyInput struct {
	Mode        string `json:"mode"`
	Timezone    string `json:"timezone"`
	Weekdays    []int  `json:"weekdays"`
	StartMinute int    `json:"start_minute"`
	EndMinute   int    `json:"end_minute"`
}

PolicyInput is what an administrator saves: a mode and a weekly window in an IANA zone.

type PolicyRun

type PolicyRun struct {
	ID       string `json:"id"`
	PolicyID string `json:"policy_id"`
	// Occurrence is the window's start, UTC: the run's identity with PolicyID.
	Occurrence    time.Time  `json:"occurrence"`
	StartedAt     time.Time  `json:"started_at"`
	FinishedAt    *time.Time `json:"finished_at"`
	Outcome       string     `json:"outcome"`
	DeploymentID  string     `json:"deployment_id"`
	Detail        string     `json:"detail"`
	CorrelationID string     `json:"correlation_id"`
	// Validation is the run's deployment's health validation, rollback included; nil without one.
	Validation *Validation `json:"validation,omitempty"`
}

type PreflightBlockedError

type PreflightBlockedError struct {
	Blockers []string
	// Services are the planned services that carry blockers of their own.
	Services []BlockedService
}

PreflightBlockedError names the findings that stopped a plan. A plan never guesses past them.

func (*PreflightBlockedError) Error

func (e *PreflightBlockedError) Error() string

type PreflightService

type PreflightService struct {
	InspectionTarget *protocol.InspectionTarget `json:"inspection_target,omitempty"`
	Name             string                     `json:"name"`
	Reference        string                     `json:"reference"`
	ImageID          string                     `json:"image_id"`
	ContainerID      string                     `json:"container_id"`
	Blockers         []string                   `json:"blockers"`
	// Mounts is the definition's volumes resolved to host names. DroppedMounts are the mapped
	// container's mounts a recreate leaves off; DroppedBinds are the binds among them.
	// UnsupportedMounts are the container's mounts the agent refuses to replace.
	Mounts            []protocol.Mount `json:"mounts"`
	DroppedBinds      []protocol.Mount `json:"dropped_binds"`
	DroppedMounts     []protocol.Mount `json:"dropped_mounts"`
	UnsupportedMounts []protocol.Mount `json:"unsupported_mounts"`
	// Unsupported are the codes a plan-time live inspection reported (configuration_unsupported).
	Unsupported []string `json:"unsupported,omitempty"`
	// Details is a code's parameter, where one has one: k8s_volume is choice_required when every
	// volume behind it is a named volume that only lacks a storage choice.
	Details map[string]string `json:"details,omitempty"`
}

type Pressure

type Pressure int32

Pressure is how close stored data is to its disk budget (docs/retention-policy.md).

const (
	// PressureNormal accepts everything.
	PressureNormal Pressure = iota
	// PressureDegraded refuses metrics: they are the cheapest telemetry to lose and the
	// fastest to grow. Inventory, heartbeats, endpoint state and audit continue.
	PressureDegraded
	// PressureStopped refuses inventory as well. Heartbeats, endpoint state and audit
	// continue, so the fleet stays visible and every refusal is still recorded.
	PressureStopped
)

func (Pressure) String

func (p Pressure) String() string

type Registry

type Registry struct {
	ID             string    `json:"id"`
	OrganizationID string    `json:"organization_id"`
	Host           string    `json:"host"`
	Name           string    `json:"name"`
	Username       string    `json:"username"`
	HasCredential  bool      `json:"has_credential"`
	AllowPrivate   bool      `json:"allow_private"`
	CreatedBy      string    `json:"created_by"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

Registry is a per-organization registry entry. The credential is write-only: rows report HasCredential and never carry the secret or its ciphertext.

type RegistryAccess

type RegistryAccess struct {
	Registry   *Registry
	Credential *registry.Credential `json:"-"` // nil when anonymous or none stored
	Anonymous  bool                 // the host has no entry and the opt-in allowed it
}

RegistryAccess is internal: it holds a decrypted secret and is never serialized.

type RegistryInput

type RegistryInput struct {
	Host         string  `json:"host"`
	Name         string  `json:"name"`
	Username     string  `json:"username"`
	Credential   *string `json:"credential"` // nil keeps the stored one, "" clears it
	AllowPrivate bool    `json:"allow_private"`
}

type RegistryPolicy

type RegistryPolicy struct {
	AnonymousPullEnabled bool `json:"anonymous_pull_enabled"`
}

type RemovalBody

type RemovalBody struct {
	InstanceID string `json:"instance_id"`
	Confirm    string `json:"confirm"`
}

type RemovalPlanTarget

type RemovalPlanTarget struct {
	Service     string `json:"service"`
	ContainerID string `json:"container_id"`
	ImageID     string `json:"image_id"`
	CreatedUnix int64  `json:"created_unix"`
	Name        string `json:"name"`
}

type Rollback

type Rollback struct {
	InstanceID     string
	MappingVersion int
	Project        string
	Revision       int
	// Images maps each service to its plan's Replaces image ID on Docker, and on a cluster to the
	// prior succeeded apply's pull reference (host/repository@sha256:...).
	Images map[string]string
}

Rollback is what an automated rollback re-applies: the revision the failed deployment replaced, each service pinned to the image it ran before.

type RollupRow

type RollupRow struct {
	ContainerID  string    `json:"container_id"`
	Hour         time.Time `json:"hour"`
	Samples      int64     `json:"samples"`
	CPUAverage   float64   `json:"cpu_avg"`
	CPUPeak      float64   `json:"cpu_max"`
	MemoryAvg    int64     `json:"memory_avg"`
	MemoryPeak   int64     `json:"memory_max"`
	RxBytes      int64     `json:"rx_bytes"`
	TxBytes      int64     `json:"tx_bytes"`
	PidsPeak     int64     `json:"pids_max"`
	RestartCount int64     `json:"restart_count"`
}

RollupRow is one container's hour, summarised. A sample the runtime would not answer for is counted in neither average nor maximum, and an hour with nothing but such samples reports -1 for that measure rather than zero, so "no data" survives the summary.

type SQLStore

type SQLStore struct {
	// contains filtered or unexported fields
}

SQLStore implements Store on top of database/sql.

func (*SQLStore) ApplyInstaller

func (s *SQLStore) ApplyInstaller(ctx context.Context, b applysetup.Common, check bool) (applysetup.Report, error)

func (*SQLStore) ApplyInstallerDirectory

func (s *SQLStore) ApplyInstallerDirectory(ctx context.Context, issuer, subject, event, revision, digest string, assigned, admin bool) error

Directory messages change availability, never account binding or role. Audit, replay fence and grant revocation commit together in the native database.

func (*SQLStore) Audit

func (s *SQLStore) Audit() AuditStore

func (*SQLStore) Budget

func (s *SQLStore) Budget() int64

Budget is the configured ceiling, zero when the check is disabled.

func (*SQLStore) Close

func (s *SQLStore) Close() error

func (*SQLStore) Devices

func (s *SQLStore) Devices() DeviceStore

func (*SQLStore) Driver

func (s *SQLStore) Driver() string

func (*SQLStore) EvaluatePressure

func (s *SQLStore) EvaluatePressure(ctx context.Context) (Pressure, error)

EvaluatePressure re-reads usage and settles the level, returning it. Degrading at 95 % leaves room to prune before anything must stop; the threshold divides before multiplying so a very large budget cannot overflow into a negative one.

What raises the level is being over budget *and still growing*. Any prune pass under pressure releases it, whether or not it freed rows.

Release cannot depend on the reading falling. A delete returns pages to the table rather than the file, a plain PostgreSQL vacuum never shrinks an index, and the reading counts data retention cannot touch at all: audit, which is never refused and not yet pruned, and inventory, where a report replaces a row rather than adding one. So once a pass has taken everything retention is owed, holding the level achieves nothing that dropping more metrics could fix — it would only refuse telemetry for the rest of the server's life.

The result at the ceiling is a throttle rather than a stop: telemetry resumes, usage grows, the level returns. That is escapable by construction, and a condition that persists is reported at intervals rather than once, because it needs an operator, not a log line.

func (*SQLStore) InstallerConfiguration

func (s *SQLStore) InstallerConfiguration(ctx context.Context) (applysetup.Common, bool, error)

func (*SQLStore) InstallerUserAllowed

func (s *SQLStore) InstallerUserAllowed(ctx context.Context, u *User) (bool, error)

func (*SQLStore) Ping

func (s *SQLStore) Ping(ctx context.Context) error

func (*SQLStore) Pressure

func (s *SQLStore) Pressure() Pressure

func (*SQLStore) RevokeInstallerIdentity

func (s *SQLStore) RevokeInstallerIdentity(ctx context.Context, issuer, subject string) error

Loss of an app-specific claim disables the account until a newer signed directory assignment explicitly restores it; a login cannot reactivate it.

func (*SQLStore) SampleCeiling

func (s *SQLStore) SampleCeiling() int

SampleCeiling is the stored-rows-per-endpoint limit this store enforces.

func (*SQLStore) Sessions

func (s *SQLStore) Sessions() SessionStore

func (*SQLStore) SetAllowPrivileged

func (s *SQLStore) SetAllowPrivileged(on bool)

func (*SQLStore) SetPressure

func (s *SQLStore) SetPressure(p Pressure)

func (*SQLStore) Settings

func (s *SQLStore) Settings() SettingsStore

func (*SQLStore) Suite added in v0.4.3

func (s *SQLStore) Suite() SuiteStore

func (*SQLStore) Tenancy

func (s *SQLStore) Tenancy() TenancyStore

func (*SQLStore) Usage

func (s *SQLStore) Usage(ctx context.Context) (int64, error)

Usage reports what the database holds, cheaply enough to read every minute: SQLite counts its pages minus the freelist, PostgreSQL reports the database size. Neither number is a measure of reclaimable data alone, which is why the level's release does not depend on it.

func (*SQLStore) Users

func (s *SQLStore) Users() UserStore

type SampleRow

type SampleRow struct {
	ContainerID  string    `json:"container_id"`
	ObservedAt   time.Time `json:"observed_at"`
	CPUPercent   float64   `json:"cpu_percent"`
	MemoryBytes  int64     `json:"memory_bytes"`
	MemoryLimit  int64     `json:"memory_limit"`
	RxBytes      int64     `json:"rx_bytes"`
	TxBytes      int64     `json:"tx_bytes"`
	Pids         int64     `json:"pids"`
	RestartCount int64     `json:"restart_count"`
}

SampleRow is a stored sample with its time; a missing row is "no data", never zero.

type ScheduledPolicy

type ScheduledPolicy struct {
	Policy UpdatePolicy
	// EndpointID is the adopted instance's endpoint; "" when the application is not adopted.
	EndpointID string
	// EndpointBusy: a deployment on that endpoint is applying, or planned and unexpired.
	EndpointBusy bool
	// Open: Occurrence is open now and has no run row.
	Open       bool
	Occurrence time.Time
	// Missed: Previous ended after the policy's last change and has no run row.
	Missed   bool
	Previous time.Time
}

ScheduledPolicy is an active policy as the scheduler sees it at one instant.

type ServiceBaseline

type ServiceBaseline struct {
	ContainerID  string         `json:"container_id"`
	RestartCount int            `json:"restart_count"`
	PodRestarts  map[string]int `json:"pod_restarts,omitempty"`
}

ServiceBaseline is one service at the first observation after grace. A cluster service keeps PodRestarts instead: each listed pod's UID and its containers' summed restarts.

type ServicePairing

type ServicePairing struct {
	ID        string    `json:"id"`
	Code      string    `json:"code"`
	ExpiresAt time.Time `json:"expires_at"`
}

ServicePairing is a six-digit code shown once to an organization administrator.

type ServiceToken

type ServiceToken struct {
	ID             string     `json:"id"`
	OrganizationID string     `json:"organization_id"`
	Name           string     `json:"name"`
	CreatedBy      string     `json:"created_by"`
	CreatedAt      time.Time  `json:"created_at"`
	LastUsedAt     *time.Time `json:"last_used_at,omitempty"`
	LastIP         string     `json:"last_ip,omitempty"`
	RevokedAt      *time.Time `json:"revoked_at,omitempty"`
}

ServiceToken is another Ky product's read-only credential for one organization. The secret is never stored: token_hash is its SHA-256.

type ServiceTokenIssue

type ServiceTokenIssue struct {
	Token        string       `json:"token"`
	Organization Organization `json:"organization"`
}

ServiceTokenIssue is the claim response: the token, once, and the organization it reads.

type Session

type Session struct {
	TokenHash string    `json:"token_hash"`
	UserID    string    `json:"user_id"`
	UserAgent string    `json:"user_agent"`
	IPAddress string    `json:"ip_address"`
	CreatedAt time.Time `json:"created_at"`
	ExpiresAt time.Time `json:"expires_at"`
}

Session represents an active authenticated user session.

type SessionStore

type SessionStore interface {
	CreateSession(ctx context.Context, s *Session, expectedPasswordHash string) error
	GetSession(ctx context.Context, tokenHash string) (*Session, error)
	DeleteSession(ctx context.Context, tokenHash string) error
	DeleteUserSessions(ctx context.Context, userID string) error
	CleanExpiredSessions(ctx context.Context) error
	CreateMFAChallenge(ctx context.Context, challenge *MFAChallenge, expectedPasswordHash string) error
	ConsumeMFAChallenge(ctx context.Context, tokenHash string) (userID, passwordHash string, err error)
}

SessionStore defines repository operations for active login sessions.

type Setting

type Setting struct {
	Key       string    `json:"key"`
	Value     string    `json:"value"`
	UpdatedAt time.Time `json:"updated_at"`
}

Setting represents a durable server-wide key-value configuration entry.

type SettingsStore

type SettingsStore interface {
	GetSetting(ctx context.Context, key string) (string, error)
	SetSetting(ctx context.Context, key, val string) error
	DeleteSetting(ctx context.Context, key string) error
	GetAllSettings(ctx context.Context) (map[string]string, error)
}

SettingsStore handles persistent key-value configuration.

type Store

type Store interface {
	InstallerConfiguration(context.Context) (applysetup.Common, bool, error)
	ApplyInstaller(context.Context, applysetup.Common, bool) (applysetup.Report, error)
	InstallerUserAllowed(context.Context, *User) (bool, error)
	ApplyInstallerDirectory(context.Context, string, string, string, string, string, bool, bool) error
	RevokeInstallerIdentity(context.Context, string, string) error
	Tenancy() TenancyStore
	Users() UserStore
	Sessions() SessionStore
	Devices() DeviceStore
	Audit() AuditStore
	Settings() SettingsStore
	Suite() SuiteStore

	Driver() string
	// SampleCeiling is the stored metric rows allowed per endpoint.
	SampleCeiling() int
	Ping(ctx context.Context) error
	Close() error
	// SetAllowPrivileged is KY_CONTAINER_ALLOW_PRIVILEGED for the application deploy path, set
	// at startup: off, a deploy never carries a host-level bind onto another image.
	SetAllowPrivileged(bool)

	// Usage reports the bytes the database occupies and Budget the ceiling it is measured
	// against, zero when the check is disabled. Pressure says what that means for telemetry;
	// audit is never refused, whatever the pressure.
	Usage(ctx context.Context) (int64, error)
	Budget() int64
	EvaluatePressure(ctx context.Context) (Pressure, error)
	Pressure() Pressure
}

Store defines the unified storage contract implemented across SQLite, PostgreSQL, and MySQL.

func Open

func Open(ctx context.Context, cfg config.DatabaseConfig) (Store, error)

Open initializes and returns the configured database store backend.

type SuiteOperation added in v0.4.3

type SuiteOperation struct {
	ID         string          `json:"id"`
	ActorID    string          `json:"actor_id"`
	Kind       string          `json:"kind"`
	Apps       []string        `json:"apps"`
	Target     string          `json:"target"`
	State      string          `json:"state"`
	StartedAt  time.Time       `json:"started"`
	FinishedAt *time.Time      `json:"finished"`
	Snapshot   json.RawMessage `json:"operation,omitempty"`
	ErrorCode  string          `json:"error_code"`
}

SuiteOperation mirrors one suite runner operation (migration 44). Snapshot is the runner's last answer for it, empty until one is read.

type SuiteStore added in v0.4.3

type SuiteStore interface {
	CreateSuiteOperation(ctx context.Context, op *SuiteOperation) error
	ReadSuiteOperation(ctx context.Context, id string) (*SuiteOperation, error)
	// UpdateSuiteOperation writes state, finish, snapshot and error code while the row is still
	// running, so a slow poll cannot reopen a settled row. A settled row is ErrNotFound.
	UpdateSuiteOperation(ctx context.Context, op *SuiteOperation) error
}

SuiteStore is trusted persistence; the API enforces suite.read and suite.manage.

type TenancyStore

type TenancyStore interface {
	ReadInspectionTarget(context.Context, TenantAccess, string, string) (protocol.InspectionTarget, error)
	// ContainerManaged (endpoint.read) reports whether an adopted application instance owns the container.
	ContainerManaged(ctx context.Context, access TenantAccess, endpointID, containerID string) (bool, error)
	// RecordConfigurationRead writes the container.configuration.read row under container.configure.
	RecordConfigurationRead(ctx context.Context, access TenantAccess, endpointID string, target protocol.InspectionTarget, unsupported int) error
	PreviewApplicationAdoption(context.Context, TenantAccess, string, string, string) (*AdoptionPreview, error)
	AdoptApplication(context.Context, TenantAccess, string, AdoptionRequest) (*ApplicationInstance, error)
	ReleaseApplication(context.Context, TenantAccess, string, string, string) error
	ListApplicationInstances(context.Context, TenantAccess, string) ([]ApplicationInstance, error)
	ReadApplicationInstance(ctx context.Context, access TenantAccess, applicationID, id string) (*ApplicationInstance, error)

	ReplaceApplicationRevision(ctx context.Context, access TenantAccess, id string, expected int, spec ApplicationSpec, values map[string]string, key []byte) (int, error)
	SetApplicationServiceIPs(ctx context.Context, access TenantAccess, id string, expected int, ips map[string]string, key []byte) (int, error)
	ImportApplication(ctx context.Context, access TenantAccess, name string, spec ApplicationSpec, values map[string]string, key []byte) (*Application, error)
	ResolveApplicationSecrets(ctx context.Context, access TenantAccess, applicationID string, number int, key []byte) (map[string]string, error)
	DiscardApplication(ctx context.Context, access TenantAccess, applicationID string, expectedRevision int) error
	CreateApplication(ctx context.Context, access TenantAccess, name string, spec ApplicationSpec) (*Application, error)
	AppendApplicationRevision(ctx context.Context, access TenantAccess, applicationID string, expectedRevision int, spec ApplicationSpec) (int, error)
	ListApplications(ctx context.Context, access TenantAccess, offset, limit int) ([]Application, error)
	PreflightApplication(ctx context.Context, access TenantAccess, applicationID string) (*DeploymentPreflight, error)
	PlanDeployment(ctx context.Context, access TenantAccess, applicationID string, request PlanRequest, resolver DigestResolver, key []byte, privateAllowed bool) (*Deployment, error)
	ReadDeployment(ctx context.Context, access TenantAccess, applicationID, id string) (*Deployment, error)
	ApplyDeployment(ctx context.Context, access TenantAccess, applicationID, id, confirm string, key []byte, maxFrameBytes int) (*Deployment, *protocol.DeploymentRequest, error)
	FailDeployment(ctx context.Context, id, detail string) error
	SettleDeployment(ctx context.Context, endpointID string, res protocol.DeploymentResult) error
	RefuseDeploymentResult(ctx context.Context, endpointID, id, detail string) error
	AbandonDeployments(ctx context.Context, endpointID string) (int64, error)
	RemoveApplication(ctx context.Context, access TenantAccess, applicationID string, body RemovalBody) (*Deployment, *protocol.RemovalRequest, error)
	ListDeployments(ctx context.Context, access TenantAccess, applicationID string) ([]Deployment, error)
	ReadApplicationMapping(ctx context.Context, access TenantAccess, applicationID string) (*ApplicationMapping, error)
	ReadImageChecks(ctx context.Context, access TenantAccess, applicationID string) (*UpdateCheck, error)
	// CheckImageUpdateAccess authorizes application.deploy on an application in scope, with
	// no success row; the API calls it before taking the per-application check slot.
	CheckImageUpdateAccess(ctx context.Context, access TenantAccess, applicationID string) error
	// CheckApplicationAccess authorizes action on an application in scope the same way, audited
	// on the application when denied; the API runs it before its runtime gate.
	CheckApplicationAccess(ctx context.Context, access TenantAccess, action permissions.Action, applicationID string) error
	// CheckImageUpdates resolves each mapped service's reference through resolver and replaces
	// the instance's cached checks, audited as application.deploy on <app>/updates.
	CheckImageUpdates(ctx context.Context, access TenantAccess, applicationID string, resolver DigestResolver, key []byte, privateAllowed bool) (*UpdateCheck, error)
	// Update policies (docs/application-schema.md, Update policies): writes under
	// application.policy, reads under application.read. ReadUpdatePolicy returns a nil policy
	// when the application has none.
	PutUpdatePolicy(ctx context.Context, access TenantAccess, applicationID string, input PolicyInput) (*UpdatePolicy, bool, error)
	ReadUpdatePolicy(ctx context.Context, access TenantAccess, applicationID string) (*UpdatePolicy, []PolicyRun, error)
	DeleteUpdatePolicy(ctx context.Context, access TenantAccess, applicationID string) error
	ResumeUpdatePolicy(ctx context.Context, access TenantAccess, applicationID string) (*UpdatePolicy, error)
	ListPolicyRuns(ctx context.Context, access TenantAccess, applicationID string, limit int) ([]PolicyRun, error)
	// The scheduler's side of update policies: trusted, not tenant-scoped. Each run re-authorizes
	// its own steps as the policy's creator.
	SchedulePolicies(ctx context.Context, now time.Time) ([]ScheduledPolicy, error)
	BeginPolicyRun(ctx context.Context, policyID string, occurrence time.Time, correlation string) (string, error)
	FinishPolicyRun(ctx context.Context, runID, outcome, deploymentID, detail string) error
	// ApplyPolicyDeployment is ApplyDeployment that also requires, in its transaction, the policy
	// still active, in apply mode and acting as access.ActorID (ErrPolicyChanged otherwise), and
	// records runID (empty for a rollback) as the run that applied the row.
	ApplyPolicyDeployment(ctx context.Context, access TenantAccess, policyID, runID, applicationID, id, confirm string, key []byte, maxFrameBytes int) (*Deployment, *protocol.DeploymentRequest, error)
	SkipPolicyWindow(ctx context.Context, policyID string, occurrence time.Time, outcome, detail string) error
	// AttachPolicyRunDeployment names the deployment an open run is about to send.
	AttachPolicyRunDeployment(ctx context.Context, runID, deploymentID string) error
	// Health validation (docs/application-schema.md, Health validation): the loop's side, trusted
	// and not tenant-scoped.
	PendingValidations(ctx context.Context) ([]PendingValidation, error)
	BeginObservation(ctx context.Context, deploymentID string, baseline map[string]ServiceBaseline) error
	// FinishValidation reports true when a rollback decision is due.
	FinishValidation(ctx context.Context, deploymentID, verdict, detail string) (bool, error)
	MarkRollbackPlanned(ctx context.Context, deploymentID, rollbackDeploymentID string) error
	MarkRollbackOutcome(ctx context.Context, deploymentID, outcome, detail string) error
	// DecideNamedRollbacks decides every named, undecided rollback whose deployment has settled.
	DecideNamedRollbacks(ctx context.Context) error
	// RollbackTarget authorizes application.deploy as the policy's creator and returns the
	// replaced revision and images, or an ineligibility reason (docs/application-schema.md, Rollback).
	RollbackTarget(ctx context.Context, access TenantAccess, applicationID, deploymentID string) (*Rollback, string, error)
	SetApplicationMapping(ctx context.Context, access TenantAccess, applicationID string, request MappingRequest) error
	// Migrations (docs/application-schema.md, Migration): writes under application.migrate,
	// audited on <app>/migration/<id>; ReadMigration under application.read. The API runs the
	// analyzer between ReadMigrationSource and the write that stores its report.
	ReadMigrationSource(ctx context.Context, access TenantAccess, applicationID, endpointID string) (*MigrationSource, error)
	CreateMigration(ctx context.Context, access TenantAccess, applicationID string, start MigrationStart, analysis MigrationAnalysis) (*ApplicationMigration, error)
	ReadMigration(ctx context.Context, access TenantAccess, applicationID string) (*ApplicationMigration, error)
	// ReadOpenMigration reads the open migration of a source under application.migrate; nil when none.
	ReadOpenMigration(ctx context.Context, access TenantAccess, applicationID string) (*ApplicationMigration, error)
	AnalyzeMigration(ctx context.Context, access TenantAccess, applicationID string, choices MigrationChoices, analysis MigrationAnalysis) (*ApplicationMigration, error)
	CreateMigrationDestination(ctx context.Context, access TenantAccess, applicationID string, key []byte) (*ApplicationMigration, error)
	ConfirmMigration(ctx context.Context, access TenantAccess, applicationID, step, note string) (*ApplicationMigration, error)
	AbandonMigration(ctx context.Context, access TenantAccess, applicationID string) (*ApplicationMigration, error)
	CompareApplication(ctx context.Context, access TenantAccess, applicationID string) (*ApplicationComparison, error)
	ReadApplicationRevision(ctx context.Context, access TenantAccess, applicationID string, number int) (*ApplicationRevision, error)
	ReadOrganization(ctx context.Context, access TenantAccess) (*Organization, error)
	ReadEnvironment(ctx context.Context, access TenantAccess) (*Environment, error)
	ListEnvironments(ctx context.Context, access TenantAccess, offset, limit int) ([]Environment, error)
	AddEnvironment(ctx context.Context, access TenantAccess, name string) (*Environment, error)
	UpdateEnvironment(ctx context.Context, access TenantAccess, name string) error
	RemoveEnvironment(ctx context.Context, access TenantAccess) error
	ReadAudit(ctx context.Context, access TenantAccess, offset, limit int) ([]AuditRecord, error)
	ReadAuditAfter(ctx context.Context, access TenantAccess, afterID int64, limit int) ([]AuditRecord, error)
	ListMembers(ctx context.Context, access TenantAccess, offset, limit int) ([]OrganizationMember, error)
	PutMembership(ctx context.Context, access TenantAccess, userID string, role TenantRole, status string) error
	RemoveMembership(ctx context.Context, access TenantAccess, userID string) error
	CreateServicePairing(ctx context.Context, access TenantAccess) (*ServicePairing, error)
	ClaimServiceToken(ctx context.Context, code, serviceName, ip string) (*ServiceTokenIssue, error)
	AuthenticateServiceToken(ctx context.Context, token, ip string) (*ServiceToken, error)
	// LookupServiceToken is AuthenticateServiceToken without the last-used stamp, for re-checks.
	LookupServiceToken(ctx context.Context, token string) (*ServiceToken, error)
	ListServiceTokens(ctx context.Context, access TenantAccess) ([]ServiceToken, error)
	RevokeServiceToken(ctx context.Context, access TenantAccess, id string) error
	// DenyService writes a denied audit row for a refusal the API decides itself (a service
	// token asking to follow a log); the store's own checks audit their denials in run.
	DenyService(ctx context.Context, access TenantAccess, action permissions.Action, detail string) error
	// RecordServiceTokenReads writes the hourly summary row for one token.
	RecordServiceTokenReads(ctx context.Context, organizationID, tokenID string, reads int) error
	ListRegistries(ctx context.Context, access TenantAccess) ([]Registry, error)
	PutRegistry(ctx context.Context, access TenantAccess, in RegistryInput, key []byte, privateAllowed bool) (*Registry, error)
	DeleteRegistry(ctx context.Context, access TenantAccess, id string) error
	ReadRegistryPolicy(ctx context.Context, access TenantAccess) (RegistryPolicy, error)
	SetAnonymousPull(ctx context.Context, access TenantAccess, enabled bool) error
	// ResolveRegistryAccess decrypts the credential for ref's host under action, the permission
	// of the operation that uses it (registry.read is refused). It audits only denials and
	// failures; callers audit the operation and never return the secret.
	ResolveRegistryAccess(ctx context.Context, access TenantAccess, action permissions.Action, ref string, key []byte, privateAllowed bool) (*RegistryAccess, error)
	// ListMemberOrganizations returns the caller's own active memberships; it is not tenant-scoped.
	ListMemberOrganizations(ctx context.Context, userID string) ([]MemberOrganization, error)
	// Platform administration: trusted, not tenant-scoped; callers enforce the platform admin role.
	// CreateOrganizationWithAdmin returns ErrNotFound (no user), ErrInvalid (user not active)
	// or ErrAlreadyExists (exact name taken).
	CreateOrganizationWithAdmin(ctx context.Context, o *Organization, adminUserID string) error
	ListOrganizations(ctx context.Context) ([]OrganizationSummary, error)

	CheckEnrollmentAccess(ctx context.Context, access TenantAccess) error
	// CreateEnrollmentToken records namespaces (Kubernetes only) for the endpoint it enrolls.
	CreateEnrollmentToken(ctx context.Context, access TenantAccess, runtime, agentImage string, namespaces ...string) (*EnrollmentToken, error)
	// SetEndpointDeployNamespaces replaces a Kubernetes endpoint's namespace list, audited as
	// <endpoint>/manifest under endpoint.enroll.
	SetEndpointDeployNamespaces(ctx context.Context, access TenantAccess, endpointID string, namespaces []string) (*Endpoint, error)
	// Enroll is agent-facing: the token, not a session, selects the tenant.
	Enroll(ctx context.Context, request EnrollmentRequest) (*Endpoint, error)
	ListEndpoints(ctx context.Context, access TenantAccess, offset, limit int) ([]Endpoint, error)
	ReadEndpoint(ctx context.Context, access TenantAccess, endpointID string) (*Endpoint, error)
	ApproveEndpoint(ctx context.Context, access TenantAccess, endpointID, fingerprint string) error
	RejectEndpoint(ctx context.Context, access TenantAccess, endpointID string) error
	RevokeEndpoint(ctx context.Context, access TenantAccess, endpointID string) error
	RenameEndpoint(ctx context.Context, access TenantAccess, endpointID, name string) error

	// Agent-facing lifecycle: authenticated by endpoint identity, never by a session.
	AgentIdentity(ctx context.Context, endpointID, fingerprint string) (*AgentIdentity, error)
	RotateEndpointKey(ctx context.Context, endpointID string, newPublicKey, signature []byte, ip string) (string, error)
	AcknowledgeEndpointKey(ctx context.Context, access TenantAccess, endpointID, fingerprint string) error
	RecordEndpointEvent(ctx context.Context, endpoint *Endpoint, severity, kind, details string) error
	AcknowledgeEndpointEvent(ctx context.Context, access TenantAccess, endpointID string, eventID int64) error
	SetEndpointCapabilities(ctx context.Context, endpointID string, capabilities []string) error
	ReadEndpointRaw(ctx context.Context, endpointID string) (*Endpoint, error)
	RecordAgentConnect(ctx context.Context, endpoint *Endpoint, ip, result, details string) error
	TouchEndpoint(ctx context.Context, endpointID string) error
	AcceptInventory(ctx context.Context, endpointID string, generation uint64, observedAt time.Time, snapshot []byte) (bool, error)
	ReadInventory(ctx context.Context, access TenantAccess, endpointID string) (*Inventory, error)
	RecordSamples(ctx context.Context, endpointID string, metrics protocol.Metrics) error
	LatestSamples(ctx context.Context, access TenantAccess, endpointID string) ([]SampleRow, error)
	ReadSamples(ctx context.Context, access TenantAccess, endpointID, containerID string, window time.Duration) ([]SampleRow, error)
	// RollUp summarises ended hours before the raw window drops them; ReadRollups serves the
	// week of history that summary buys.
	// Commands are durable intent: the row exists before the frame is sent, so an operation
	// the control plane loses track of can still be marked unknown rather than vanish.
	CreateCommand(ctx context.Context, access TenantAccess, endpointID, action, containerID, confirm string, expects protocol.Expectation) (*Command, error)
	CheckEndpointAccess(ctx context.Context, access TenantAccess, action permissions.Action, endpointID string) error
	OpenExecTarget(ctx context.Context, access TenantAccess, endpointID, streamID, confirm string, spec protocol.ExecSpec) (string, error)
	OpenLogTarget(ctx context.Context, access TenantAccess, endpointID, identifier string) (*LogTarget, error)
	OpenPodLogTarget(ctx context.Context, access TenantAccess, endpointID string, pod protocol.PodTarget) error
	StillAllowed(ctx context.Context, access TenantAccess, action permissions.Action, endpointID string) error
	MarkCommandDispatched(ctx context.Context, id string) error
	SettleCommand(ctx context.Context, endpointID, id, outcome, detail string) error
	// Direct commands (container.recreate, container.run) are explicit deployment frames recorded
	// as commands under container.configure and settled from the agent's deployment.result.
	CreateDirectCommand(ctx context.Context, access TenantAccess, endpointID string, dc DirectCommand) (*Command, *protocol.DeploymentRequest, error)
	ResolveDirectImage(ctx context.Context, access TenantAccess, endpointID, reference string, resolver DigestResolver, key []byte, privateAllowed bool) (*protocol.ImagePull, map[string]protocol.RegistryAuth, error)
	SettleDirectCommand(ctx context.Context, endpointID string, res protocol.DeploymentResult) error
	CheckDirectCommand(ctx context.Context, access TenantAccess, endpointID string, dc DirectCommand) error
	// Workload configuration and apply: a cluster workload's pod template, read and applied
	// under container.configure; the apply is a direct command settled from deployment.result.
	CheckWorkloadTarget(ctx context.Context, access TenantAccess, action permissions.Action, endpointID string, ref protocol.WorkloadRef) error
	RecordWorkloadConfigurationRead(ctx context.Context, access TenantAccess, endpointID string, ref protocol.WorkloadRef, unsupported int) error
	CreateWorkloadApply(ctx context.Context, access TenantAccess, endpointID string, w WorkloadApply) (*Command, *protocol.WorkloadApply, error)
	CreateWorkloadRun(ctx context.Context, access TenantAccess, endpointID string, w WorkloadApply) (*Command, *protocol.WorkloadApply, error)
	CheckWorkloadFrame(ctx context.Context, access TenantAccess, endpointID string, w WorkloadApply, create bool) error
	FailDirectCommand(ctx context.Context, endpointID, id, detail string) error
	AbandonCommands(ctx context.Context, endpointID string) (int64, error)
	// ReconcileAfterStart settles every in-flight command as unknown, fails every open policy run
	// and settles validations the previous process left; startup only.
	ReconcileAfterStart(ctx context.Context) (int64, error)
	ReadCommand(ctx context.Context, access TenantAccess, endpointID, id string) (*Command, error)
	ListCommands(ctx context.Context, access TenantAccess, endpointID, containerID, reference string, limit int) ([]Command, error)
	RollUp(ctx context.Context, since time.Time) (int64, error)
	ReadRollups(ctx context.Context, access TenantAccess, endpointID, containerID string, window time.Duration) ([]RollupRow, error)
	Prune(ctx context.Context) (int64, error)
	MarkEndpointOffline(ctx context.Context, endpointID string) error
	EndpointState(ctx context.Context, endpointID string) (string, error)

	Initialize(ctx context.Context) error
	InitializeLocalDocker(ctx context.Context, publicKey []byte) (uint64, error)
	CreateOrganization(ctx context.Context, organization *Organization) error
	GetOrganization(ctx context.Context, organizationID string) (*Organization, error)
	SetMembership(ctx context.Context, membership *OrganizationMembership) error
	GetMembership(ctx context.Context, organizationID, userID string) (*OrganizationMembership, error)
	DeleteMembership(ctx context.Context, organizationID, userID string) error
	CreateEnvironment(ctx context.Context, environment *Environment) error
	GetEnvironment(ctx context.Context, organizationID, environmentID string) (*Environment, error)
	RenameEnvironment(ctx context.Context, organizationID, environmentID, name string) error
	DeleteEnvironment(ctx context.Context, organizationID, environmentID string) error
	CreateOrganizationGroup(ctx context.Context, group *OrganizationGroup) error
	AddOrganizationGroupMember(ctx context.Context, organizationID, groupID, userID string) error
	ListOrganizationGroupMembers(ctx context.Context, organizationID, groupID string) ([]string, error)
	RemoveOrganizationGroupMember(ctx context.Context, organizationID, groupID, userID string) error
}

TenancyStore provides authorized operations through TenantAccess. The raw methods below Initialize are trusted persistence helpers for bootstrap and management.

type TenantAccess

type TenantAccess struct {
	ActorID        string
	OrganizationID string
	EnvironmentID  string
	CorrelationID  string
	IPAddress      string
	// ServiceTokenID is set instead of ActorID for a service token; run resolves its fixed role.
	ServiceTokenID string
}

TenantAccess is server-derived request context. Clients cannot choose ActorID or CorrelationID.

func (TenantAccess) Principal

func (a TenantAccess) Principal() string

Principal names the caller for a per-caller key: rate limits, stream slots, agent grants and audit rows. A service token has no ActorID, so keying on it alone would collapse every service token into one shared bucket.

type TenantRole

type TenantRole string

Platform authority remains User.Role; it never implies one of these tenant roles.

const (
	RoleOrganizationAdmin TenantRole = "organization_admin"
	RoleEnvironmentAdmin  TenantRole = "environment_admin"
	RoleOperator          TenantRole = "operator"
	RoleDeveloper         TenantRole = "developer"
	RoleReadOnly          TenantRole = "read_only"
	InitialOrganizationID            = "org_initial"
)

type UpdateCheck

type UpdateCheck struct {
	InstanceID     string       `json:"instance_id"`
	MappingVersion int          `json:"mapping_version"`
	Services       []ImageCheck `json:"services"`
}

UpdateCheck is an instance's cached checks; InstanceID is empty when nothing is adopted.

type UpdatePolicy

type UpdatePolicy struct {
	ID                  string    `json:"id"`
	ApplicationID       string    `json:"application_id"`
	CreatedBy           string    `json:"created_by"`
	Mode                string    `json:"mode"`
	Timezone            string    `json:"timezone"`
	Weekdays            []int     `json:"weekdays"`
	StartMinute         int       `json:"start_minute"`
	EndMinute           int       `json:"end_minute"`
	Status              string    `json:"status"`
	PausedReason        string    `json:"paused_reason"`
	ConsecutiveFailures int       `json:"consecutive_failures"`
	CreatedAt           time.Time `json:"created_at"`
	UpdatedAt           time.Time `json:"updated_at"`
	// NextOccurrence is computed on read, UTC; nil while paused.
	NextOccurrence *time.Time `json:"next_occurrence"`
	OrganizationID string     `json:"-"`
	EnvironmentID  string     `json:"-"`
}

type User

type User struct {
	ID                 string     `json:"id"`
	Username           string     `json:"username"`
	Email              string     `json:"email"`
	DisplayName        string     `json:"display_name"`
	PasswordHash       string     `json:"-"`            // Never serialized to JSON
	Role               string     `json:"role"`         // "admin", "user", "manager"
	Status             string     `json:"status"`       // "active", "suspended", "inactive"
	SSOProvider        string     `json:"sso_provider"` // "local", "kysignon", "oidc", "saml", "scim"
	SSOSubject         string     `json:"sso_subject,omitempty"`
	TOTPSecretEnc      string     `json:"-"` // AES-256-GCM encrypted
	TOTPEnabled        bool       `json:"totp_enabled"`
	TOTPLastCounter    int64      `json:"-"` // last RFC 6238 counter accepted; refuses replay inside the skew window
	RecoveryCodesHash  string     `json:"-"` // JSON array of sha256 hashes
	PushDeviceID       string     `json:"push_device_id,omitempty"`
	MustChangePassword bool       `json:"must_change_password"`
	CreatedAt          time.Time  `json:"created_at"`
	UpdatedAt          time.Time  `json:"updated_at"`
	LastLoginAt        *time.Time `json:"last_login_at,omitempty"`
}

User represents an identity within the system.

type UserStore

type UserStore interface {
	CreateUser(ctx context.Context, u *User) error
	GetUserByID(ctx context.Context, id string) (*User, error)
	GetUserByUsername(ctx context.Context, username string) (*User, error)
	GetUserByEmail(ctx context.Context, email string) (*User, error)
	GetUserBySSO(ctx context.Context, provider, subject string) (*User, error)
	LinkSSO(ctx context.Context, userID, sessionHash, passwordHash, provider, subject, ip string) error
	// UpdateUser writes every column from u, credentials included: a trusted full-row write for
	// fixtures. The identity provider writes through ApplyDirectoryEvent instead.
	UpdateUser(ctx context.Context, u *User) error
	ResetAdminPassword(ctx context.Context, userID, newHash string) error
	CompletePasswordChange(ctx context.Context, userID, oldHash, newHash, ip string) error
	// ChangePassword is CompletePasswordChange for an unflagged account: same revocation and audit.
	ChangePassword(ctx context.Context, userID, oldHash, newHash, ip string) error
	UpdateRecoveryCodes(ctx context.Context, userID, oldHashes, newHashes string) error
	// UpdateMFA compares and swaps the MFA state under the password hash and audits action.
	UpdateMFA(ctx context.Context, userID, passwordHash string, from, to MFAState, action, ip string) error
	// SpendTOTPCounter records counter as used. It returns ErrAlreadyExists when counter is
	// not greater than the stored one, which is how a replayed code inside the skew window fails.
	SpendTOTPCounter(ctx context.Context, userID string, counter int64) error
	// DeleteUser is a local deletion: it refuses with ErrLastAdmin to leave an organization the
	// user administers without an active administrator, the guard membership writes keep.
	DeleteUser(ctx context.Context, id string) error
	// ApplyDirectoryEvent claims ev and runs apply in one transaction: the replay marker, the
	// subject's watermark and apply's writes commit together or not at all, so a failed or
	// cancelled event leaves nothing behind and the provider's retry applies. It returns
	// ErrStaleEvent for an event ID already applied, or one older than the last applied event
	// for the same subject. Events are only acceptable within DirectoryEventWindow of now.
	// apply must not use the Store itself: it would wait on this transaction.
	ApplyDirectoryEvent(ctx context.Context, ev DirectoryEvent, apply func(DirectoryTx) error) error
	ListUsers(ctx context.Context, offset, limit int) ([]*User, int, error)
	CountUsers(ctx context.Context) (int, error)
}

UserStore defines repository operations for accounts.

type Validation

type Validation struct {
	DeploymentID  string              `json:"deployment_id"`
	PolicyRunID   string              `json:"policy_run_id"`
	Automated     bool                `json:"automated"`
	IsRollback    bool                `json:"is_rollback"`
	Phase         string              `json:"phase"`
	StartedAt     time.Time           `json:"started_at"`
	ObserveUntil  time.Time           `json:"observe_until"`
	Verdict       string              `json:"verdict"`
	Detail        string              `json:"detail"`
	Rollback      *ValidationRollback `json:"rollback"`
	CorrelationID string              `json:"correlation_id"`
	FinishedAt    *time.Time          `json:"finished_at"`
}

Validation is a deployment's health validation as the API shows it: the row without its baseline and tenant columns. Rollback is nil until a rollback is named or decided.

type ValidationRollback

type ValidationRollback struct {
	DeploymentID string `json:"deployment_id"`
	// Revision is the rollback deployment's; 0 once that row is gone.
	Revision int    `json:"revision"`
	Outcome  string `json:"outcome"`
	Detail   string `json:"detail"`
}

type WorkloadApply

type WorkloadApply struct {
	Target  protocol.WorkloadRef
	Confirm string
	Spec    protocol.WorkloadConfiguration
}

WorkloadApply is an edited pod template as the API received it: Spec.ResourceVersion is the read's (empty for a run), and Confirm repeats the workload's name.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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