model

package
v0.1.39 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package model contains Boxy's core domain data models.

Documentation pillars for this package: 1) What the thing is (definition) 2) Why it exists (the problem it solves) 3) When to use it (where it belongs in the system) 4) How to use it correctly (invariants and intended mutation points)

Design intent:

  • Keep these types "data-first": plain structs and small enums.
  • Prefer explicit domain types (PoolName, SandboxID, ResourceType) over raw strings when it prevents mixing up identifiers.
  • Keep behavior and side effects out of the model layer. Orchestrators, pool controllers, providers, agents, and storage adapters should depend on this package, not the other way around.

---

Provider

What:

  • A provider instance is an external system Boxy can provision resources on. Examples: a specific Docker engine, or a specific Hyper-V host.

Why:

  • Boxy must be able to route lifecycle operations (create/inspect/destroy) to the correct external system over time.

When to use: - Use ProviderRef on domain objects that need routing ("which provider owns this?"). - Use the runtime config layer (providersdk.Instance) for provider connection/config.

How to use correctly:

  • ProviderRef.Name must match a configured provider instance name.
  • Avoid using the word "provider" to mean both the code and the external system. In this repo:
  • provider instance = external configured target (providersdk.Instance)
  • ProviderRef = reference from core domain objects
  • driver/adapter = code that talks to a provider type

---

Pool

What: - Pool is a user-facing container of Resources.

Why:

  • Pools give Boxy a place to keep "ready-to-allocate" inventory, which is the foundation of preheating (min_ready) and fast sandbox allocation.

When to use:

  • Use Pool when you want the primary CLI/config noun to be something humans can name ("dev-docker", "win11-vms") and reason about as a bucket.
  • Use Pool inventory when you need to allocate a Resource into a Sandbox or decide whether to provision more inventory.

How to use correctly:

  • Pools should be homogeneous: all Resources in a Pool inventory should share the same ResourceType. (See ResourceCollection.)
  • Resources are single-use: once a Resource leaves a Pool for a Sandbox, it is not returned to any Pool.
  • The model layer does not replenish pools. A controller/reconciler should enforce preheat policy and perform lifecycle transitions.

---

Resource

What: - Resource is a tracked, provisioned instance (VM/container/share/etc).

Why:

  • Boxy needs a stable, backend-agnostic representation of "a thing that exists" so it can allocate it, track its lifecycle, and eventually clean it up.

When to use:

  • Use Resource for anything that can be allocated to a Sandbox and has a lifecycle state.

How to use correctly:

  • Resource.Type is intrinsic: a resource "is a VM" regardless of which pool or sandbox currently references it.
  • If you want homogeneous containers, enforce that at the container boundary (ResourceCollection.Add / Validate) rather than trying to infer a resource's kind from the container it happens to be in.

---

Sandbox

What: - Sandbox is a user-facing environment over 1..N resources, treated as "one thing".

Why:

  • Users want one handle they can name and destroy even when the environment contains multiple resources (VM + DB + share).
  • A sandbox can be as small as a single resource ("container sandbox") or as large as a full lab ("3 VM lab sandbox").

When to use: - Use Sandbox as the primary lifecycle object in the system.

How to use correctly:

  • Sandboxes should reference concrete allocated items; avoid hiding "what was allocated from where" since that is required for cleanup and troubleshooting.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIKey added in v0.1.34

type APIKey struct {
	ID        APIKeyID   `json:"id" yaml:"id"`
	Hash      string     `json:"hash" yaml:"hash"`
	Role      APIKeyRole `json:"role" yaml:"role"`
	Name      string     `json:"name,omitempty" yaml:"name,omitempty"`
	CreatedAt time.Time  `json:"created_at" yaml:"created_at"`
	ExpiresAt *time.Time `json:"expires_at,omitempty" yaml:"expires_at,omitempty"`
	RevokedAt *time.Time `json:"revoked_at,omitempty" yaml:"revoked_at,omitempty"`
}

APIKey is the persisted metadata for an operator credential. The raw key is deliberately never part of this model; Hash is the only credential material stored by the daemon.

func (APIKey) Expired added in v0.1.34

func (k APIKey) Expired(now time.Time) bool

Expired reports whether the key has an expiry in the past.

func (APIKey) Revoked added in v0.1.34

func (k APIKey) Revoked() bool

Revoked reports whether the key has been explicitly revoked.

type APIKeyID added in v0.1.34

type APIKeyID string

APIKeyID identifies an operator API key record.

type APIKeyRole added in v0.1.34

type APIKeyRole string

APIKeyRole controls the operations available to an authenticated API key.

const (
	APIKeyRoleUser    APIKeyRole = "user"
	APIKeyRoleAuditor APIKeyRole = "auditor"
	APIKeyRoleAdmin   APIKeyRole = "admin"
)

func (APIKeyRole) Valid added in v0.1.34

func (r APIKeyRole) Valid() bool

Valid reports whether the role is one of Boxy's supported API-key roles.

type AgentIdentity

type AgentIdentity struct {
	AgentID    string    `json:"agent_id" yaml:"agent_id"`
	CertSerial string    `json:"cert_serial" yaml:"cert_serial"`
	IssuedAt   time.Time `json:"issued_at" yaml:"issued_at"`
}

AgentIdentity records which client certificate serial is currently associated with a registered agent, so an operator can revoke an agent by ID (`boxy agent revoke <id>`) even while it's disconnected — without this, the server would only learn an agent's cert serial while it has a live connection open.

type AgentIdentityID

type AgentIdentityID string

AgentIdentityID identifies a revoked-agent-identity record.

type AgentRegistrationToken

type AgentRegistrationToken struct {
	ID        AgentTokenID `json:"id" yaml:"id"`
	TokenHash string       `json:"token_hash" yaml:"token_hash"`
	CreatedAt time.Time    `json:"created_at" yaml:"created_at"`
	ExpiresAt time.Time    `json:"expires_at" yaml:"expires_at"`
	UsedAt    *time.Time   `json:"used_at,omitempty" yaml:"used_at,omitempty"`
	Label     string       `json:"label,omitempty" yaml:"label,omitempty"`
}

AgentRegistrationToken is a single-use, short-lived bootstrap credential minted by an operator (`boxy agent token create`) and consumed exactly once by `boxy agent serve --token ...` during initial registration. The raw token is never persisted — only its hash, so a store dump does not itself let someone impersonate an agent.

func (AgentRegistrationToken) Expired

func (t AgentRegistrationToken) Expired(now time.Time) bool

Expired reports whether this token's expiry has passed as of now.

func (AgentRegistrationToken) Used

func (t AgentRegistrationToken) Used() bool

Used reports whether this token has already been redeemed.

type AgentTokenID

type AgentTokenID string

AgentTokenID identifies a registration token record.

type Pool

type Pool struct {
	Name PoolName `json:"name" yaml:"name"`

	// Policies are pool-level behavioral controls (preheating, limits, etc).
	Policies PoolPolicies `json:"policies,omitempty" yaml:"policies,omitempty"`

	// Drain records desired drain state for unused pool inventory.
	Drain PoolDrainState `json:"drain,omitempty" yaml:"drain,omitempty"`

	// Inventory is the current contents of the pool.
	Inventory ResourceCollection `json:"inventory" yaml:"inventory"`
}

Pool is a user-facing container of resources.

func (Pool) EffectivelyDrained

func (p Pool) EffectivelyDrained() bool

EffectivelyDrained reports whether the pool should currently be drained.

type PoolDrainState

type PoolDrainState struct {
	// ConfigDeclared is true when config declares the pool drained.
	ConfigDeclared bool `json:"config_declared,omitempty" yaml:"config_declared,omitempty"`

	// Operator is a persisted operator/debug override.
	Operator bool `json:"operator,omitempty" yaml:"operator,omitempty"`
}

PoolDrainState records whether a pool should destroy and avoid creating unused ready inventory.

func (PoolDrainState) Effective

func (d PoolDrainState) Effective() bool

Effective reports whether either config or operator state keeps the pool drained.

type PoolName

type PoolName string

PoolName is the stable, user-facing handle for a pool (the thing typed in CLI/config).

type PoolPolicies

type PoolPolicies struct {
	// Preheat controls keeping resources ready ahead of time.
	Preheat PreheatPolicy `json:"preheat,omitempty" yaml:"preheat,omitempty"`

	// Recycle controls periodic replacement of unused pool inventory.
	//
	// This does NOT mean resources are returned to the pool after sandbox use.
	// Resources remain single-use; recycle applies only to unused inventory.
	Recycle RecyclePolicy `json:"recycle,omitempty" yaml:"recycle,omitempty"`
}

PoolPolicies captures pool-level behavior without prescribing a specific CLI/API surface yet.

type PreheatPolicy

type PreheatPolicy struct {
	// MinReady is the number of ready units the pool should try to keep available.
	MinReady int `json:"min_ready,omitempty" yaml:"min_ready,omitempty"`

	// MaxTotal is the maximum total units that may exist for the pool.
	MaxTotal int `json:"max_total,omitempty" yaml:"max_total,omitempty"`
}

PreheatPolicy is the pool policy for keeping resources ready ahead of time.

type Profile

type Profile struct {
	Type ResourceType    `json:"type" yaml:"type"`
	Name ResourceProfile `json:"name" yaml:"name"`
}

Profile is a catalog entry defining an allowed (Type, Profile) pair.

This is intentionally minimal scaffolding. It exists to prevent "random strings at call sites" from becoming the de facto API.

func (Profile) Validate

func (p Profile) Validate() error

type ProfileRegistry

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

ProfileRegistry is a simple in-memory catalog of supported profiles.

It is Boxy-owned (not provider-owned): a profile name is a stable identifier that Boxy components can match on across pools and requests.

func NewProfileRegistry

func NewProfileRegistry(profiles []Profile) (*ProfileRegistry, error)

func (*ProfileRegistry) Has

type ProviderRef

type ProviderRef struct {
	Name string `json:"name" yaml:"name"`

	// AgentID is the specific agent instance (embedded or remote) that
	// provisioned this resource, stamped once at creation time. It is
	// deliberately distinct from Name (the provider type/instance): once
	// more than one agent can advertise the same provider type, any
	// lifecycle call on an existing resource (Destroy, Allocate) must
	// route back to this exact agent rather than re-resolving by type,
	// or it risks silently misrouting to a different agent that knows
	// nothing about the resource. See docs/adr/0005-remote-agent-transport-and-registration.md.
	AgentID string `json:"agent_id,omitempty" yaml:"agent_id,omitempty"`
}

ProviderRef identifies the provider instance that owns a resource.

This intentionally does not embed provider connection/config data. The canonical configured providers live in the runtime config (see providersdk.Instance).

For now, Name is the stable handle and must match a configured provider name. If/when renames become a real feature, introduce an immutable ID here.

type RecyclePolicy

type RecyclePolicy struct {
	// MaxAge is an optional upper bound on how long an unused resource may sit in
	// a pool before it should be recycled (destroy + replace).
	// Example values: "30m", "8h", "24h".
	MaxAge string `json:"max_age,omitempty" yaml:"max_age,omitempty"`
}

RecyclePolicy describes when unused resources should be destroyed and replaced.

type Resource

type Resource struct {
	ID ResourceID `json:"id,omitempty" yaml:"id,omitempty"`

	// Type is intrinsic to the resource, independent of which container holds it.
	Type ResourceType `json:"type,omitempty" yaml:"type,omitempty"`

	// Profile is a Boxy-defined variant identifier for this resource's Type.
	// Example: vm "win-2022", container "ubuntu-2204".
	Profile ResourceProfile `json:"profile,omitempty" yaml:"profile,omitempty"`

	// OriginPool is the immutable pool that originally provisioned this resource.
	// It is used for pool-level capacity accounting even after allocation removes
	// the resource from ready inventory.
	OriginPool PoolName `json:"origin_pool,omitempty" yaml:"origin_pool,omitempty"`

	// Provider identifies the external system instance this resource belongs to.
	Provider ProviderRef `json:"provider" yaml:"provider"`

	State ResourceState `json:"state,omitempty" yaml:"state,omitempty"`

	// Properties holds provider-specific data that Boxy core should not interpret.
	Properties map[string]any `json:"properties,omitempty" yaml:"properties,omitempty"`

	CreatedAt time.Time `json:"created_at,omitempty" yaml:"created_at,omitempty"`
	UpdatedAt time.Time `json:"updated_at,omitempty" yaml:"updated_at,omitempty"`
}

Resource is a provisioned instance tracked by Boxy.

type ResourceCollection

type ResourceCollection struct {
	// ExpectedType is a constraint: all Resources must have Resource.Type == ExpectedType.
	ExpectedType ResourceType `json:"expected_type" yaml:"expected_type"`

	// ExpectedProfile is a constraint: all Resources must have Resource.Profile == ExpectedProfile.
	ExpectedProfile ResourceProfile `json:"expected_profile" yaml:"expected_profile"`

	Resources []Resource `json:"resources,omitempty" yaml:"resources,omitempty"`
}

ResourceCollection is a homogeneous container of resources.

func (*ResourceCollection) Add

func (c *ResourceCollection) Add(r Resource) error

Add appends r if it satisfies the collection invariant.

func (ResourceCollection) Validate

func (c ResourceCollection) Validate() error

Validate checks that the collection is internally consistent.

type ResourceID

type ResourceID string

ResourceID is a stable identifier for a resource tracked by Boxy.

type ResourceProfile

type ResourceProfile string

ResourceProfile is a Boxy-defined variant identifier for a ResourceType.

Think of it as the "pooling key" beyond the coarse ResourceType: - VM: "win-2022", "ubuntu-2204", "ubuntu-2204-devbox" - Container: "ubuntu-2204", "ubuntu-2204-lamp" - Share: "default", "smb-basic"

Profiles are type-dependent by design; "win-2022" only makes sense for vm.

const (
	// ResourceProfileDefault is the conventional "no special customization" profile.
	//
	// Using an explicit default keeps pools deterministic (no implicit meaning for "").
	ResourceProfileDefault ResourceProfile = "default"
)

type ResourceRequest

type ResourceRequest struct {
	Type    ResourceType    `json:"type" yaml:"type"`
	Profile ResourceProfile `json:"profile" yaml:"profile"`
	Count   int             `json:"count" yaml:"count"`
}

ResourceRequest expresses demand for one or more resources matching a (Type, Profile) key.

This is intentionally small scaffolding: - It is not a full "spec" or "recipe". - It exists to make pooling/matching deterministic beyond coarse ResourceType.

Examples:

  • {Type: vm, Profile: "win-2022", Count: 3}
  • {Type: container, Profile: "ubuntu-2204", Count: 1}
  • {Type: share, Profile: "default", Count: 1}

func (ResourceRequest) Validate

func (r ResourceRequest) Validate() error

type ResourceState

type ResourceState string

ResourceState is the lifecycle state of a resource.

const (
	ResourceStateUnknown      ResourceState = "unknown"
	ResourceStateProvisioning ResourceState = "provisioning"
	ResourceStateReady        ResourceState = "ready"
	ResourceStateAllocated    ResourceState = "allocated"
	ResourceStateReleased     ResourceState = "released"
	// ResourceStateRecycling marks a resource being torn down by pool-level
	// max-age recycling (see RecyclePolicy) specifically, distinct from
	// ResourceStateDestroying (drain and explicit sandbox-triggered destroy).
	// It exists purely for observability: without it, a resource mid-teardown
	// looks identical to a healthy one (via the REST API, CLI, or persisted
	// state) until it suddenly disappears, which is indistinguishable from a
	// stalled/hung backend.
	ResourceStateRecycling  ResourceState = "recycling"
	ResourceStateDestroying ResourceState = "destroying"
	ResourceStateDestroyed  ResourceState = "destroyed"
	ResourceStateError      ResourceState = "error"
)

type ResourceType

type ResourceType string

ResourceType is the domain category of a resource (vm/container/share/etc).

const (
	ResourceTypeUnknown   ResourceType = "unknown"
	ResourceTypeVM        ResourceType = "vm"
	ResourceTypeContainer ResourceType = "container"
	ResourceTypeShare     ResourceType = "share"
	ResourceTypeNetwork   ResourceType = "network"
	ResourceTypeDB        ResourceType = "db"
)

type RevokedAgentIdentity

type RevokedAgentIdentity struct {
	ID         AgentIdentityID `json:"id" yaml:"id"`
	AgentID    string          `json:"agent_id" yaml:"agent_id"`
	CertSerial string          `json:"cert_serial" yaml:"cert_serial"`
	RevokedAt  time.Time       `json:"revoked_at" yaml:"revoked_at"`
	Reason     string          `json:"reason,omitempty" yaml:"reason,omitempty"`
}

RevokedAgentIdentity is a deny-list entry: once an agent's client certificate serial is revoked, the server must refuse new connections presenting that certificate, regardless of expiry, and tear down any live connection using it.

type Sandbox

type Sandbox struct {
	ID      SandboxID `json:"id" yaml:"id"`
	Name    string    `json:"name,omitempty" yaml:"name,omitempty"`
	OwnerID string    `json:"owner_id,omitempty" yaml:"owner_id,omitempty"`

	// Policies are sandbox-level behavioral controls (security, retention, etc).
	Policies SandboxPolicies `json:"policies,omitzero" yaml:"policies,omitempty"`

	// Status is the async lifecycle state of the sandbox request.
	Status SandboxStatus `json:"status,omitempty" yaml:"status,omitempty"`

	// Requests are the desired resources for this sandbox.
	Requests []ResourceRequest `json:"requests,omitempty" yaml:"requests,omitempty"`

	// Error is a human-readable failure detail when Status=failed.
	Error string `json:"error,omitempty" yaml:"error,omitempty"`

	// Resources are the resources that make up this sandbox.
	Resources []ResourceID `json:"resources,omitempty" yaml:"resources,omitempty"`

	// ExpiresAt is the absolute time this sandbox should be automatically
	// destroyed, computed from Policies.AutoDestroyAfter at creation time.
	// Nil means no automatic expiry.
	ExpiresAt *time.Time `json:"expires_at,omitempty" yaml:"expires_at,omitempty"`
}

Sandbox is a user-facing environment that contains 1..N resources.

This model is intentionally minimal. Orchestration state and richer composition semantics are layered on later.

type SandboxID

type SandboxID string

SandboxID is a stable identifier for a sandbox (user-facing handle).

type SandboxPolicies

type SandboxPolicies struct {
	// AutoDestroyAfter is an optional retention setting (e.g. "30m", "8h").
	// Empty means "no policy set here".
	AutoDestroyAfter string `json:"auto_destroy_after,omitempty" yaml:"auto_destroy_after,omitempty"`

	// SecurityProfile is an optional label for sandbox hardening posture.
	// Examples: "default", "lab", "pentest", "vdi".
	SecurityProfile string `json:"security_profile,omitempty" yaml:"security_profile,omitempty"`
}

SandboxPolicies captures sandbox-level behavior without prescribing a specific CLI/API surface yet.

type SandboxStatus

type SandboxStatus string

SandboxStatus is the lifecycle state of a sandbox request.

const (
	SandboxStatusPending      SandboxStatus = "pending"
	SandboxStatusProvisioning SandboxStatus = "provisioning"
	SandboxStatusReady        SandboxStatus = "ready"
	SandboxStatusDeleting     SandboxStatus = "deleting"
	SandboxStatusFailed       SandboxStatus = "failed"
)

Jump to

Keyboard shortcuts

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