Documentation
¶
Overview ¶
Package control owns the Phase 4 control-plane model: owners, apps, deploys, auth metadata, and deploy pointers. It deliberately avoids transport and database choices so HTTP handlers and persistence can be layered on later.
Index ¶
- Constants
- Variables
- func ValidateDigest(label, digest string) error
- func ValidateEgressHost(host string) (string, error)
- func ValidateName(name string) error
- type App
- type AppInput
- type Artifact
- type ArtifactInput
- type AuthIdentity
- type AuthProvider
- type BlobStore
- type CIToken
- type CITokenInput
- type Deploy
- type DeployClaimInput
- type DeployInput
- type DeployedApp
- type IdentityInput
- type Option
- func WithAnonymousPreview(enabled bool) Option
- func WithBlobDir(dir string) Option
- func WithClock(now func() time.Time) Option
- func WithDefaultMaxApps(n int) Option
- func WithDeployClaimTTL(d time.Duration) Option
- func WithMaxDeployClaimsPerHour(n int) Option
- func WithMaxSessionsPerAppPerDay(n int) Option
- func WithPreviousSnapshotEncryptionKey(key []byte) Option
- func WithSnapshotEncryptionKey(key []byte) Option
- type Owner
- type Quotas
- type SSHKey
- type SSHKeyInput
- type SecretInput
- type SecretMetadata
- type Session
- type Store
- func (s *Store) ActivateDeploy(appID, deployID string) (App, error)
- func (s *Store) AddEgressHost(appID, host string) ([]string, error)
- func (s *Store) AnonymousPreviewEnabled() bool
- func (s *Store) AppDailyConnections(appID string) (used, cap int)
- func (s *Store) AuthenticateCIToken(tokenHash string) (CIToken, Owner, error)
- func (s *Store) AuthorizeDeployClaimUpdate(deployID, appName, sourceDigest, claimTokenHash string) error
- func (s *Store) ClaimDeploy(deployID, claimTokenHash, ownerID string) (App, Deploy, string, error)
- func (s *Store) ClaimOwnerHandle(ownerID, handle string) (Owner, error)
- func (s *Store) CreateApp(in AppInput) (App, error)
- func (s *Store) CreateArtifact(in ArtifactInput) (Artifact, error)
- func (s *Store) CreateCIToken(in CITokenInput) (CIToken, error)
- func (s *Store) CreateDeploy(in DeployInput) (Deploy, error)
- func (s *Store) CreateDeployClaim(in DeployClaimInput) (Deploy, error)
- func (s *Store) CreateDeployClaimWithArtifact(reservation uint64, artifactIn ArtifactInput, wasm []byte, ...) (Artifact, Deploy, error)
- func (s *Store) CreateOwner(handle string) (Owner, error)
- func (s *Store) DeleteExpiredDeployClaims() (int, error)
- func (s *Store) DeleteSecret(appID, key string) error
- func (s *Store) DeployClaimTTL() time.Duration
- func (s *Store) EgressAllowlist(appID string) []string
- func (s *Store) EndSession(id string) (Session, error)
- func (s *Store) EnsureApp(in AppInput) (App, error)
- func (s *Store) EnsureAutoClaimOwner() (Owner, error)
- func (s *Store) EnsureOwner(handle string) (Owner, error)
- func (s *Store) EnsureOwnerForIdentity(in IdentityInput) (Owner, AuthIdentity, error)
- func (s *Store) FindOwner(handle string) (Owner, error)
- func (s *Store) GetArtifact(id string) (Artifact, error)
- func (s *Store) GetCIToken(id string) (CIToken, error)
- func (s *Store) GetDeploy(id string) (Deploy, error)
- func (s *Store) GetOwner(id string) (Owner, error)
- func (s *Store) InspectDeployClaim(deployID, claimTokenHash string) (Deploy, App, Owner, Artifact, error)
- func (s *Store) ListApps(ownerID string) ([]App, error)
- func (s *Store) ListCITokens(ownerID string) ([]CIToken, error)
- func (s *Store) ListDeployedApps() []DeployedApp
- func (s *Store) ListSSHKeys(ownerID string) ([]SSHKey, error)
- func (s *Store) ListSecrets(appID string) []SecretMetadata
- func (s *Store) ListSessionsForApp(appID string) ([]Session, error)
- func (s *Store) LoadSnapshot(path string) error
- func (s *Store) PutArtifactBytes(artifactID string, wasm []byte) error
- func (s *Store) RecordSessionLog(id, log string, truncated bool) (Session, error)
- func (s *Store) RegisterSSHKey(in SSHKeyInput) (SSHKey, error)
- func (s *Store) RegisterSuspensionListener(listener SuspensionListener) func()
- func (s *Store) RemoveEgressHost(appID, host string) ([]string, error)
- func (s *Store) ReserveDeployClaimQuota() (uint64, func(), error)
- func (s *Store) ResolveActiveDeploy(ownerHandle, appName string) (App, Deploy, Artifact, error)
- func (s *Store) ResolveRunnable(handle string) (App, Deploy, Artifact, []byte, error)
- func (s *Store) ResolveSSHKey(fingerprint string) (SSHKey, Owner, error)
- func (s *Store) RevokeCIToken(ownerID, id string) (CIToken, error)
- func (s *Store) RevokeSSHKey(ownerID, id string) error
- func (s *Store) SecretsForApp(appID string) map[string]string
- func (s *Store) SetAppSuspended(appID string, suspended bool) (App, error)
- func (s *Store) SetDeploySuspended(deployID string, suspended bool) error
- func (s *Store) SetOwnerSuspended(ownerID string, suspended bool) (Owner, error)
- func (s *Store) StartSession(appID, deployID string) (Session, error)
- func (s *Store) Subscribe() (<-chan struct{}, func())
- func (s *Store) UpdateDeployClaim(deployID string, in DeployClaimInput) (App, Deploy, bool, error)
- func (s *Store) UpdateDeployClaimWithArtifact(deployID string, artifactIn ArtifactInput, wasm []byte, ...) (Artifact, App, Deploy, bool, error)
- func (s *Store) UpsertSecret(in SecretInput) (SecretMetadata, error)
- type SuspensionEvent
- type SuspensionListener
- type SuspensionScope
- type TokenScope
Constants ¶
const AutoClaimOwnerHandle = "autoclaim"
AutoClaimOwnerHandle is the internal owner used for deployments accepted without an interactive user claim. Its apps are exposed publicly by bare app name even though they remain grouped under this owner for storage and policy.
const DeployClaimTTL = 5 * time.Minute
DeployClaimTTL is the default lifetime of an unclaimed deploy before it is garbage-collected. Operators can override it per-store with WithDeployClaimTTL.
Variables ¶
Functions ¶
func ValidateDigest ¶
ValidateDigest validates a SHA-256 digest supplied at an API boundary.
func ValidateEgressHost ¶
ValidateEgressHost normalizes and validates an egress allowlist entry. It accepts a public DNS hostname (optionally with a leading dot for the subdomain-wildcard form) and rejects IP literals, single-label/internal names, and anything carrying a scheme, port, or path. This is the add-time half of the SSRF defense; the runner additionally blocks non-public resolved IPs at dial time to stop DNS rebinding.
func ValidateName ¶
ValidateName accepts the namespace-safe handles used for owners and apps.
Types ¶
type App ¶
type App struct {
ID string
OwnerID string
Name string
ActiveDeployID string
// Suspended is an operator kill switch: when true, the app does not resolve
// to a runnable session.
Suspended bool
CreatedAt time.Time
}
App is the durable namespace record for <owner>/<name>.
type Artifact ¶
type Artifact struct {
ID string
Digest string
SizeBytes int64
ABIVersion uint8
BuildMetadata map[string]string
CreatedAt time.Time
}
Artifact describes a content-addressed WASM artifact produced by build workers. The control plane stores metadata only, never raw source or WASM.
type ArtifactInput ¶
type AuthIdentity ¶
type AuthIdentity struct {
Provider AuthProvider
Subject string
OwnerID string
CreatedAt time.Time
}
AuthIdentity binds an external verified identity to a Plumtree owner.
type BlobStore ¶
type BlobStore interface {
Put(id string, data []byte) error
Get(id string) ([]byte, bool)
Delete(id string)
// contains filtered or unexported methods
}
BlobStore holds artifact bytes (compiled WASM). It is separated from the metadata store so large binaries need not live inside the JSON state file: a durable, filesystem-backed store keeps them on disk, while the default in-memory store embeds them in the snapshot for the all-in-one dev process.
type CIToken ¶
type CIToken struct {
ID string
OwnerID string
Name string
TokenHash string
Scopes []TokenScope
CreatedAt time.Time
RevokedAt *time.Time
}
CIToken is metadata for an owner-scoped automation token.
type CITokenInput ¶
type CITokenInput struct {
OwnerID string
Name string
TokenHash string
Scopes []TokenScope
}
type Deploy ¶
type Deploy struct {
ID string
AppID string
AppName string
AppType string
ArtifactID string
SourceDigest string
CreatedByOwnerID string
ClaimTokenHash string
CreatedAt time.Time
ClaimExpiresAt *time.Time
ClaimedAt *time.Time
}
Deploy is an immutable release record for an app.
type DeployClaimInput ¶
type DeployInput ¶
type DeployedApp ¶
DeployedApp pairs an active app with its namespace owner for server-level administrative listings.
type IdentityInput ¶
type IdentityInput struct {
Provider AuthProvider
Subject string
}
type Option ¶
type Option func(*Store)
func WithAnonymousPreview ¶
WithAnonymousPreview enables anonymous preview run: any deploy is runnable by id at "preview-<deployID>" in the tightest sandbox (no owner capabilities). It is gated because it lets unclaimed code run; enable it only with deploy rate limiting in place.
func WithBlobDir ¶
WithBlobDir stores compiled WASM artifacts as files under dir instead of inside the JSON state file — durable artifact storage that keeps large binaries out of the metadata snapshot. The directory is created on first use.
func WithDefaultMaxApps ¶
WithDefaultMaxApps caps how many apps a single owner may create, applied to any owner without an explicit MaxApps quota. 0 leaves owners uncapped.
func WithDeployClaimTTL ¶
WithDeployClaimTTL sets how long an unclaimed deploy survives before it is garbage-collected. Non-positive values keep the default (DeployClaimTTL).
func WithMaxDeployClaimsPerHour ¶
WithMaxDeployClaimsPerHour caps how many new deploy claims may be created across the platform in any rolling hour — the primary control against anonymous-deploy flooding (deploy is gated harder than run). 0 disables it.
func WithMaxSessionsPerAppPerDay ¶
WithMaxSessionsPerAppPerDay caps how many sessions a single app may start in any rolling 24-hour window, a platform-wide abuse/DDoS control. 0 disables it.
func WithPreviousSnapshotEncryptionKey ¶
WithPreviousSnapshotEncryptionKey permits a one-time startup migration from a snapshot wrapped by the previous KEK. New writes always use the current key configured by WithSnapshotEncryptionKey.
func WithSnapshotEncryptionKey ¶
WithSnapshotEncryptionKey enables envelope encryption for the durable state snapshot. The 32-byte KEK must come from a managed secret store or a file mounted outside the state volume.
type Owner ¶
type Owner struct {
ID string
Handle string
HandleClaimed bool
// Internal distinguishes reserved platform namespaces from user-owned
// handles. Internal owners cannot be created or claimed through public flows.
Internal bool
// Suspended is an operator kill switch: when true, none of the owner's apps
// resolve to a runnable session.
Suspended bool
CreatedAt time.Time
}
Owner is an authenticated Plumtree namespace owner.
type Quotas ¶
Quotas are owner-level abuse and cost limits. Zero values mean "unset" in this in-memory implementation.
type SSHKey ¶
type SSHKey struct {
ID string
OwnerID string
Name string
PublicKey string
Fingerprint string
CreatedAt time.Time
}
SSHKey is login metadata for owner authentication.
type SSHKeyInput ¶
type SecretInput ¶
type SecretMetadata ¶
type SecretMetadata struct {
AppID string
Key string
Version int
CreatedAt time.Time
UpdatedAt time.Time
}
SecretMetadata records the existence/version of a server-side app secret. Secret values are intentionally outside this package.
type Session ¶
type Session struct {
ID string
AppID string
DeployID string
StartedAt time.Time
EndedAt *time.Time
// Log is the guest's captured stdout/stderr for the session, size-capped by
// the runner. LogTruncated reports that output exceeded the cap and the tail
// was dropped.
Log string
LogTruncated bool
}
Session records a runner session selected by the gateway/control plane.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a concurrency-safe control-plane repository. It is in-memory by default; OpenStore enables local JSON snapshot persistence. A SQL implementation can satisfy the same behavior later.
func OpenStore ¶
OpenStore returns a store backed by snapshotPath. A missing path starts empty; subsequent durable mutations atomically rewrite the snapshot file.
func (*Store) ActivateDeploy ¶
ActivateDeploy moves an app's active deploy pointer after proving the deploy belongs to the same app. Deploy records themselves remain immutable.
func (*Store) AddEgressHost ¶
AddEgressHost adds host to the app's allowlist if absent. It returns the updated allowlist.
func (*Store) AnonymousPreviewEnabled ¶
AnonymousPreviewEnabled reports whether anonymous preview run is on, so the API can advertise the preview handle to the deploy client.
func (*Store) AppDailyConnections ¶
AppDailyConnections reports how many sessions the app started in the trailing 24h window and the configured per-app daily cap. A cap of 0 means unlimited.
func (*Store) AuthenticateCIToken ¶
AuthenticateCIToken resolves an active (non-revoked) CI token by its hash and returns the token with its owner. Revoked and unknown hashes both report ErrNotFound so callers cannot distinguish them.
func (*Store) AuthorizeDeployClaimUpdate ¶
func (s *Store) AuthorizeDeployClaimUpdate(deployID, appName, sourceDigest, claimTokenHash string) error
AuthorizeDeployClaimUpdate validates an update and its claim proof without requiring an artifact, so callers can reject it before invoking a builder.
func (*Store) ClaimDeploy ¶
func (*Store) ClaimOwnerHandle ¶
func (*Store) CreateArtifact ¶
func (s *Store) CreateArtifact(in ArtifactInput) (Artifact, error)
func (*Store) CreateCIToken ¶
func (s *Store) CreateCIToken(in CITokenInput) (CIToken, error)
func (*Store) CreateDeploy ¶
func (s *Store) CreateDeploy(in DeployInput) (Deploy, error)
func (*Store) CreateDeployClaim ¶
func (s *Store) CreateDeployClaim(in DeployClaimInput) (Deploy, error)
func (*Store) CreateDeployClaimWithArtifact ¶
func (s *Store) CreateDeployClaimWithArtifact(reservation uint64, artifactIn ArtifactInput, wasm []byte, deployIn DeployClaimInput) (Artifact, Deploy, error)
CreateDeployClaimWithArtifact commits artifact metadata, bytes, and the new deploy claim as one store operation. reservation must have been obtained before building with ReserveDeployClaimQuota.
func (*Store) DeleteExpiredDeployClaims ¶
func (*Store) DeleteSecret ¶
DeleteSecret removes a secret (metadata and value). Deleting a missing secret reports ErrNotFound.
func (*Store) DeployClaimTTL ¶
DeployClaimTTL returns the configured unclaimed-deploy lifetime, so callers (e.g. cleanup schedulers) stay consistent with the store's expiry policy.
func (*Store) EgressAllowlist ¶
EgressAllowlist returns the app's outbound-HTTP host allowlist (a copy).
func (*Store) EnsureAutoClaimOwner ¶
EnsureAutoClaimOwner returns the reserved internal owner used for trusted automatic claims. A pre-existing public owner with the reserved handle is rejected instead of receiving deployments intended for the platform owner.
func (*Store) EnsureOwnerForIdentity ¶
func (s *Store) EnsureOwnerForIdentity(in IdentityInput) (Owner, AuthIdentity, error)
func (*Store) InspectDeployClaim ¶
func (*Store) ListCITokens ¶
ListCITokens returns an owner's CI tokens (active and revoked) ordered by ID.
func (*Store) ListDeployedApps ¶
func (s *Store) ListDeployedApps() []DeployedApp
ListDeployedApps returns all claimed apps with an active deploy, ordered by their owner/app handle.
func (*Store) ListSSHKeys ¶
ListSSHKeys returns an owner's registered SSH keys ordered by ID.
func (*Store) ListSecrets ¶
func (s *Store) ListSecrets(appID string) []SecretMetadata
ListSecrets returns the value-free metadata for an app's secrets, sorted by key. Values are never returned.
func (*Store) ListSessionsForApp ¶
func (*Store) LoadSnapshot ¶
LoadSnapshot replaces the current store contents with the snapshot at path. Missing files are treated as an empty store.
func (*Store) PutArtifactBytes ¶
func (*Store) RecordSessionLog ¶
RecordSessionLog stores the guest's captured output for a session. truncated reports that the runner dropped output past its size cap. It is safe to call after EndSession.
func (*Store) RegisterSSHKey ¶
func (s *Store) RegisterSSHKey(in SSHKeyInput) (SSHKey, error)
func (*Store) RegisterSuspensionListener ¶
func (s *Store) RegisterSuspensionListener(listener SuspensionListener) func()
RegisterSuspensionListener adds one suspension destination. The returned function unregisters it and is safe to call more than once.
func (*Store) RemoveEgressHost ¶
RemoveEgressHost drops host from the app's allowlist. Removing an absent host is not an error.
func (*Store) ReserveDeployClaimQuota ¶
ReserveDeployClaimQuota reserves capacity in the rolling deploy-claim rate limit before expensive build work starts. The returned release function is idempotent; a successful CreateDeployClaimWithArtifact consumes the ticket.
func (*Store) ResolveActiveDeploy ¶
func (*Store) ResolveRunnable ¶
func (*Store) ResolveSSHKey ¶
ResolveSSHKey resolves a proved SSH public-key fingerprint to its registered key and owner. Callers must only use this after the SSH transport has verified a signature made by the corresponding private key.
func (*Store) RevokeCIToken ¶
RevokeCIToken marks an owner's CI token revoked. It is idempotent and fails with ErrNotFound if the token is unknown or owned by someone else.
func (*Store) RevokeSSHKey ¶
RevokeSSHKey removes one of an owner's registered SSH keys. A key owned by someone else is reported as not found so ownership cannot be probed.
func (*Store) SecretsForApp ¶
SecretsForApp returns the app's secret keys and values, for injection into a claimed app's runtime as the Env capability. This is the only method that exposes values, and it stays inside the platform (never reaches an HTTP response).
func (*Store) SetAppSuspended ¶
SetAppSuspended toggles the app-level kill switch.
func (*Store) SetDeploySuspended ¶
SetDeploySuspended toggles the deploy-level kill switch. The deploy record is unchanged; suspension is tracked separately so deploy records stay immutable.
func (*Store) SetOwnerSuspended ¶
SetOwnerSuspended toggles the owner-level kill switch. While suspended, none of the owner's apps resolve to a runnable session.
func (*Store) Subscribe ¶
func (s *Store) Subscribe() (<-chan struct{}, func())
Subscribe registers a listener for runtime-state changes. The returned channel receives a coalesced signal whenever sessions change; the caller re-reads the store to get the new state. The cancel func unregisters and must be called when the listener goes away.
func (*Store) UpdateDeployClaim ¶
func (*Store) UpdateDeployClaimWithArtifact ¶
func (s *Store) UpdateDeployClaimWithArtifact(deployID string, artifactIn ArtifactInput, wasm []byte, deployIn DeployClaimInput) (Artifact, App, Deploy, bool, error)
UpdateDeployClaimWithArtifact authenticates again under the commit lock and switches the deploy to a newly-created artifact atomically.
func (*Store) UpsertSecret ¶
func (s *Store) UpsertSecret(in SecretInput) (SecretMetadata, error)
type SuspensionEvent ¶
type SuspensionEvent struct {
Scope SuspensionScope `json:"scope"`
ID string `json:"id"`
}
type SuspensionListener ¶
type SuspensionListener func(SuspensionEvent) error
SuspensionListener acknowledges an event by returning nil only after all matching sessions under its responsibility have stopped.
type SuspensionScope ¶
type SuspensionScope string
const ( SuspensionOwner SuspensionScope = "owner" SuspensionApp SuspensionScope = "app" SuspensionDeploy SuspensionScope = "deploy" )
type TokenScope ¶
type TokenScope string
TokenScope limits CI token use. Tokens are stored by hash only.
const ( ScopeDeploy TokenScope = "deploy" ScopeInspect TokenScope = "inspect" ScopeLogs TokenScope = "logs" ScopeSecrets TokenScope = "secrets" )
func ParseScope ¶
func ParseScope(raw string) (TokenScope, error)
ParseScope validates and normalizes a raw CI token scope string.