Documentation
¶
Overview ¶
Package builds implements registry token generation for secure builder VM authentication.
Package builds implements a secure build system that runs rootless BuildKit inside ephemeral Cloud Hypervisor microVMs for multi-tenant isolation.
Index ¶
- Constants
- Variables
- func GetCacheKeyFromConfig(registryURL, cacheScope, runtime string, lockfileHashes map[string]string) (importArg, exportArg string, err error)
- func ValidateCacheScope(scope string) error
- type Build
- type BuildConfig
- type BuildEvent
- type BuildPolicy
- type BuildProvenance
- type BuildQueue
- func (q *BuildQueue) ActiveCount() int
- func (q *BuildQueue) Cancel(buildID string) bool
- func (q *BuildQueue) Enqueue(buildID string, req CreateBuildRequest, startFn func()) int
- func (q *BuildQueue) GetPosition(buildID string) *int
- func (q *BuildQueue) IsActive(buildID string) bool
- func (q *BuildQueue) MarkComplete(buildID string)
- func (q *BuildQueue) PendingCount() int
- func (q *BuildQueue) QueueLength() int
- type BuildResult
- type CacheKey
- type CacheKeyGenerator
- type Config
- type CreateBuildRequest
- type FileSecretProvider
- type Manager
- type Metrics
- type NoOpSecretProvider
- type QueuedBuild
- type RegistryTokenClaims
- type RegistryTokenGenerator
- type SecretProvider
- type SecretRef
- type SecretsRequest
- type SecretsResponse
- type VsockMessage
Constants ¶
const ( StatusQueued = "queued" StatusBuilding = "building" StatusPushing = "pushing" StatusReady = "ready" StatusFailed = "failed" StatusCancelled = "cancelled" )
Build status constants
const ( EventTypeLog = "log" EventTypeStatus = "status" EventTypeHeartbeat = "heartbeat" )
BuildEvent type constants
const ( // BuildAgentVsockPort is the port the builder agent listens on inside the guest BuildAgentVsockPort = 5001 // SecretsVsockPort is the port the host listens on for secret requests from builder agents SecretsVsockPort = 5002 )
Variables ¶
var ( // ErrNotFound is returned when a build is not found ErrNotFound = errors.New("build not found") // ErrAlreadyExists is returned when a build with the same ID already exists ErrAlreadyExists = errors.New("build already exists") // ErrDockerfileRequired is returned when no Dockerfile is provided ErrDockerfileRequired = errors.New("dockerfile required: provide dockerfile parameter or include Dockerfile in source tarball") // ErrBuildFailed is returned when a build fails ErrBuildFailed = errors.New("build failed") // ErrBuildTimeout is returned when a build exceeds its timeout ErrBuildTimeout = errors.New("build timeout") // ErrBuildCancelled is returned when a build is cancelled ErrBuildCancelled = errors.New("build cancelled") // ErrInvalidSource is returned when the source tarball is invalid ErrInvalidSource = errors.New("invalid source") // ErrSourceHashMismatch is returned when the source hash doesn't match ErrSourceHashMismatch = errors.New("source hash mismatch") // ErrBuilderNotReady is returned when the builder image is not available ErrBuilderNotReady = errors.New("builder image not ready") // ErrBuildInProgress is returned when trying to cancel a build that's already complete ErrBuildInProgress = errors.New("build in progress") )
Functions ¶
func GetCacheKeyFromConfig ¶
func GetCacheKeyFromConfig(registryURL, cacheScope, runtime string, lockfileHashes map[string]string) (importArg, exportArg string, err error)
GetCacheKeyFromConfig extracts cache configuration for the builder agent
func ValidateCacheScope ¶
ValidateCacheScope validates that a cache scope is safe to use
Types ¶
type Build ¶
type Build struct {
ID string `json:"id"`
Status string `json:"status"`
QueuePosition *int `json:"queue_position,omitempty"`
ImageDigest *string `json:"image_digest,omitempty"`
ImageRef *string `json:"image_ref,omitempty"`
Error *string `json:"error,omitempty"`
Provenance *BuildProvenance `json:"provenance,omitempty"`
CreatedAt time.Time `json:"created_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
DurationMS *int64 `json:"duration_ms,omitempty"`
}
Build represents a source-to-image build job
type BuildConfig ¶
type BuildConfig struct {
// JobID is the build job identifier
JobID string `json:"job_id"`
// Dockerfile content (if not provided in source tarball)
Dockerfile string `json:"dockerfile,omitempty"`
// BaseImageDigest optionally pins the base image
BaseImageDigest string `json:"base_image_digest,omitempty"`
// RegistryURL is where to push the built image
RegistryURL string `json:"registry_url"`
// RegistryToken is a short-lived JWT granting push access to specific repositories.
// The builder agent uses this token to authenticate with the registry.
RegistryToken string `json:"registry_token,omitempty"`
// CacheScope is the tenant-specific cache key prefix
CacheScope string `json:"cache_scope,omitempty"`
// SourcePath is the path to source in the guest (typically /src)
SourcePath string `json:"source_path"`
// BuildArgs are ARG values for the Dockerfile
BuildArgs map[string]string `json:"build_args,omitempty"`
// Secrets are secret references to fetch from host
Secrets []SecretRef `json:"secrets,omitempty"`
// TimeoutSeconds is the build timeout
TimeoutSeconds int `json:"timeout_seconds"`
// NetworkMode is "isolated" or "egress"
NetworkMode string `json:"network_mode"`
}
BuildConfig is the configuration passed to the builder VM via config disk This is read by the builder agent inside the guest
type BuildEvent ¶
type BuildEvent struct {
// Type is one of "log", "status", or "heartbeat"
Type string `json:"type"`
// Timestamp is when the event occurred
Timestamp time.Time `json:"timestamp"`
// Content is the log line content (only for type="log")
Content string `json:"content,omitempty"`
// Status is the new build status (only for type="status")
Status string `json:"status,omitempty"`
}
BuildEvent represents a typed SSE event for build streaming
type BuildPolicy ¶
type BuildPolicy struct {
// TimeoutSeconds is the maximum build duration (default: 600)
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
// MemoryMB is the memory limit for the builder VM (default: 2048)
MemoryMB int `json:"memory_mb,omitempty"`
// CPUs is the number of vCPUs for the builder VM (default: 2)
CPUs int `json:"cpus,omitempty"`
// NetworkMode controls network access during build
// "isolated" = no network, "egress" = outbound allowed
NetworkMode string `json:"network_mode,omitempty"`
// AllowedDomains restricts egress to specific domains (only when NetworkMode="egress")
AllowedDomains []string `json:"allowed_domains,omitempty"`
}
BuildPolicy defines resource limits and network policy for a build
func DefaultBuildPolicy ¶
func DefaultBuildPolicy() BuildPolicy
DefaultBuildPolicy returns the default build policy
func (*BuildPolicy) ApplyDefaults ¶
func (p *BuildPolicy) ApplyDefaults()
ApplyDefaults fills in default values for a build policy
type BuildProvenance ¶
type BuildProvenance struct {
// BaseImageDigest is the pinned base image used
BaseImageDigest string `json:"base_image_digest"`
// SourceHash is the SHA256 of the source tarball
SourceHash string `json:"source_hash"`
// LockfileHashes maps lockfile names to their SHA256 hashes
LockfileHashes map[string]string `json:"lockfile_hashes,omitempty"`
// BuildkitVersion is the BuildKit version used
BuildkitVersion string `json:"buildkit_version,omitempty"`
// Timestamp is when the build completed
Timestamp time.Time `json:"timestamp"`
}
BuildProvenance records the inputs and toolchain used for a build This enables reproducibility verification and audit trails
type BuildQueue ¶
type BuildQueue struct {
// contains filtered or unexported fields
}
BuildQueue manages concurrent builds with a configurable limit. Following the pattern from lib/images/queue.go.
Design notes (see plan for full context): - Queue state is in-memory (lost on restart) - Build metadata is persisted to disk - On startup, pending builds are recovered via listPendingBuilds()
Future migration path if needed: - Add BuildQueue interface with Enqueue/Dequeue/Ack/Nack - Implement adapters: memoryQueue, redisQueue, natsQueue - Use BUILD_QUEUE_BACKEND env var to select implementation
func NewBuildQueue ¶
func NewBuildQueue(maxConcurrent int) *BuildQueue
NewBuildQueue creates a new build queue with the given concurrency limit
func (*BuildQueue) ActiveCount ¶
func (q *BuildQueue) ActiveCount() int
ActiveCount returns the number of actively building builds
func (*BuildQueue) Cancel ¶
func (q *BuildQueue) Cancel(buildID string) bool
Cancel removes a build from the pending queue. Returns true if the build was cancelled, false if it was not in the queue (already running or not found).
func (*BuildQueue) Enqueue ¶
func (q *BuildQueue) Enqueue(buildID string, req CreateBuildRequest, startFn func()) int
Enqueue adds a build to the queue. Returns queue position (0 if started immediately, >0 if queued). If the build is already building or queued, returns its current position without re-enqueueing.
func (*BuildQueue) GetPosition ¶
func (q *BuildQueue) GetPosition(buildID string) *int
GetPosition returns the queue position for a build. Returns nil if the build is actively running or not in queue.
func (*BuildQueue) IsActive ¶
func (q *BuildQueue) IsActive(buildID string) bool
IsActive returns true if the build is actively running
func (*BuildQueue) MarkComplete ¶
func (q *BuildQueue) MarkComplete(buildID string)
MarkComplete marks a build as complete and starts the next pending build if any
func (*BuildQueue) PendingCount ¶
func (q *BuildQueue) PendingCount() int
PendingCount returns the number of queued builds
func (*BuildQueue) QueueLength ¶
func (q *BuildQueue) QueueLength() int
QueueLength returns the total number of builds (active + pending)
type BuildResult ¶
type BuildResult struct {
// Success indicates whether the build succeeded
Success bool `json:"success"`
// ImageDigest is the digest of the pushed image (only on success)
ImageDigest string `json:"image_digest,omitempty"`
// Error is the error message (only on failure)
Error string `json:"error,omitempty"`
// Logs is the full build log output
Logs string `json:"logs,omitempty"`
// Provenance records build inputs for reproducibility
Provenance BuildProvenance `json:"provenance"`
// DurationMS is the build duration in milliseconds
DurationMS int64 `json:"duration_ms"`
}
BuildResult is returned by the builder agent after a build completes
type CacheKey ¶
type CacheKey struct {
// Full reference for BuildKit --import-cache / --export-cache
Reference string
// Components
TenantScope string
Runtime string
LockfileHash string
}
CacheKey represents a validated cache key
func (*CacheKey) ExportCacheArg ¶
ExportCacheArg returns the BuildKit --export-cache argument
func (*CacheKey) ImportCacheArg ¶
ImportCacheArg returns the BuildKit --import-cache argument
type CacheKeyGenerator ¶
type CacheKeyGenerator struct {
// contains filtered or unexported fields
}
CacheKeyGenerator generates cache keys for builds with tenant isolation
func NewCacheKeyGenerator ¶
func NewCacheKeyGenerator(registryURL string) *CacheKeyGenerator
NewCacheKeyGenerator creates a new cache key generator
func (*CacheKeyGenerator) GenerateCacheKey ¶
func (g *CacheKeyGenerator) GenerateCacheKey(tenantScope, runtime string, lockfileHashes map[string]string) (*CacheKey, error)
GenerateCacheKey generates a cache key for a build.
Cache key structure:
{registry}/cache/{tenant_scope}/{runtime}/{lockfile_hash}
This structure provides: - Tenant isolation: each tenant's cache is isolated by scope - Runtime separation: Node.js and Python caches don't mix - Lockfile-based keying: same lockfile = cache hit
type Config ¶
type Config struct {
// MaxConcurrentBuilds is the maximum number of concurrent builds
MaxConcurrentBuilds int
// BuilderImage is the OCI image to use for builder VMs
// This should contain rootless BuildKit and the builder agent
BuilderImage string
// RegistryURL is the URL of the registry to push built images to
RegistryURL string
// DefaultTimeout is the default build timeout in seconds
DefaultTimeout int
// RegistrySecret is the secret used to sign registry access tokens
// This should be the same secret used by the registry middleware
RegistrySecret string
}
Config holds configuration for the build manager
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the default build manager configuration
type CreateBuildRequest ¶
type CreateBuildRequest struct {
// Dockerfile content. Required if not included in the source tarball.
// The Dockerfile specifies the runtime (e.g., FROM node:20-alpine).
Dockerfile string `json:"dockerfile,omitempty"`
// BaseImageDigest optionally pins the base image by digest for reproducibility
BaseImageDigest string `json:"base_image_digest,omitempty"`
// SourceHash is the SHA256 hash of the source tarball for verification
SourceHash string `json:"source_hash,omitempty"`
// BuildPolicy contains resource limits and network policy for the build
BuildPolicy *BuildPolicy `json:"build_policy,omitempty"`
// CacheScope is the tenant-specific cache key prefix for isolation
CacheScope string `json:"cache_scope,omitempty"`
// BuildArgs are ARG values to pass to the Dockerfile
BuildArgs map[string]string `json:"build_args,omitempty"`
// Secrets are secret references to inject during build
Secrets []SecretRef `json:"secrets,omitempty"`
}
CreateBuildRequest represents a request to create a new build
type FileSecretProvider ¶
type FileSecretProvider struct {
// contains filtered or unexported fields
}
FileSecretProvider reads secrets from files in a directory. Each secret is stored as a file named by its ID, with the secret value as the file content. Example: /etc/hypeman/secrets/npm_token contains the npm token value.
func NewFileSecretProvider ¶
func NewFileSecretProvider(secretsDir string) *FileSecretProvider
NewFileSecretProvider creates a new file-based secret provider. secretsDir is the directory containing secret files (e.g., /etc/hypeman/secrets/).
func (*FileSecretProvider) GetSecrets ¶
func (p *FileSecretProvider) GetSecrets(ctx context.Context, secretIDs []string) (map[string]string, error)
GetSecrets returns the values for the given secret IDs by reading files from the secrets directory. Missing secrets are silently skipped (not an error). Returns an error only if a secret file exists but cannot be read.
type Manager ¶
type Manager interface {
// Start starts the build manager's background services (vsock handler, etc.)
// This should be called once when the API server starts.
Start(ctx context.Context) error
// CreateBuild starts a new build job
CreateBuild(ctx context.Context, req CreateBuildRequest, sourceData []byte) (*Build, error)
// GetBuild returns a build by ID
GetBuild(ctx context.Context, id string) (*Build, error)
// ListBuilds returns all builds
ListBuilds(ctx context.Context) ([]*Build, error)
// CancelBuild cancels a pending or running build
CancelBuild(ctx context.Context, id string) error
// GetBuildLogs returns the logs for a build
GetBuildLogs(ctx context.Context, id string) ([]byte, error)
// StreamBuildEvents streams build events (logs, status changes, heartbeats)
// With follow=false, returns existing logs then closes
// With follow=true, continues streaming until build completes or context cancels
StreamBuildEvents(ctx context.Context, id string, follow bool) (<-chan BuildEvent, error)
// RecoverPendingBuilds recovers builds that were interrupted on restart
RecoverPendingBuilds()
}
Manager interface for the build system
type Metrics ¶
type Metrics struct {
// contains filtered or unexported fields
}
Metrics provides Prometheus metrics for the build system
func NewMetrics ¶
NewMetrics creates a new Metrics instance
func (*Metrics) RecordBuild ¶
RecordBuild records metrics for a completed build
func (*Metrics) RegisterQueueCallbacks ¶
func (m *Metrics) RegisterQueueCallbacks(queue *BuildQueue, meter metric.Meter) error
RegisterQueueCallbacks registers callbacks for queue metrics
type NoOpSecretProvider ¶
type NoOpSecretProvider struct{}
NoOpSecretProvider returns empty secrets (for builds without secrets)
func (*NoOpSecretProvider) GetSecrets ¶
type QueuedBuild ¶
type QueuedBuild struct {
BuildID string
Request CreateBuildRequest
StartFn func()
}
QueuedBuild represents a build waiting to be executed
type RegistryTokenClaims ¶
type RegistryTokenClaims struct {
jwt.RegisteredClaims
// BuildID is the build job identifier for audit purposes
BuildID string `json:"build_id"`
// Repositories is the list of allowed repository paths (e.g., ["builds/abc123", "cache/tenant-x"])
Repositories []string `json:"repos"`
// Scope is the access scope: "push" for write access, "pull" for read-only
Scope string `json:"scope"`
}
RegistryTokenClaims contains the claims for a scoped registry access token. These tokens are issued to builder VMs to grant limited push access to specific repositories.
func (*RegistryTokenClaims) IsPullAllowed ¶
func (c *RegistryTokenClaims) IsPullAllowed() bool
IsPullAllowed returns true if the token grants pull (read) access. Push tokens also implicitly grant pull access.
func (*RegistryTokenClaims) IsPushAllowed ¶
func (c *RegistryTokenClaims) IsPushAllowed() bool
IsPushAllowed returns true if the token grants push (write) access.
func (*RegistryTokenClaims) IsRepositoryAllowed ¶
func (c *RegistryTokenClaims) IsRepositoryAllowed(repo string) bool
IsRepositoryAllowed checks if the given repository path is allowed by the token claims.
type RegistryTokenGenerator ¶
type RegistryTokenGenerator struct {
// contains filtered or unexported fields
}
RegistryTokenGenerator creates scoped registry access tokens
func NewRegistryTokenGenerator ¶
func NewRegistryTokenGenerator(secret string) *RegistryTokenGenerator
NewRegistryTokenGenerator creates a new token generator with the given secret
func (*RegistryTokenGenerator) GeneratePushToken ¶
func (g *RegistryTokenGenerator) GeneratePushToken(buildID string, repos []string, ttl time.Duration) (string, error)
GeneratePushToken creates a short-lived token granting push access to specific repositories. The token expires after the specified duration (typically matching the build timeout).
func (*RegistryTokenGenerator) ValidateToken ¶
func (g *RegistryTokenGenerator) ValidateToken(tokenString string) (*RegistryTokenClaims, error)
ValidateToken parses and validates a registry token, returning the claims if valid.
type SecretProvider ¶
type SecretProvider interface {
// GetSecrets returns the values for the given secret IDs
GetSecrets(ctx context.Context, secretIDs []string) (map[string]string, error)
}
SecretProvider provides secrets for builds
type SecretRef ¶
type SecretRef struct {
// ID is the secret identifier (used in --mount=type=secret,id=...)
ID string `json:"id"`
// EnvVar is the environment variable name to expose the secret as
EnvVar string `json:"env_var,omitempty"`
}
SecretRef references a secret to inject during build
type SecretsRequest ¶
type SecretsRequest struct {
SecretIDs []string `json:"secret_ids"`
}
SecretsRequest is sent by the builder agent to fetch secrets
type SecretsResponse ¶
SecretsResponse contains the requested secrets
type VsockMessage ¶
type VsockMessage struct {
Type string `json:"type"`
Result *BuildResult `json:"result,omitempty"`
Log string `json:"log,omitempty"`
SecretIDs []string `json:"secret_ids,omitempty"` // For secrets request
Secrets map[string]string `json:"secrets,omitempty"` // For secrets response
}
VsockMessage is the envelope for vsock communication with builder agents
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package main implements the builder agent that runs inside builder microVMs.
|
Package main implements the builder agent that runs inside builder microVMs. |