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 ¶
- type APIKey
- type APIKeyID
- type APIKeyRole
- type AgentIdentity
- type AgentIdentityID
- type AgentRegistrationToken
- type AgentTokenID
- type Pool
- type PoolDrainState
- type PoolName
- type PoolPolicies
- type PreheatPolicy
- type Profile
- type ProfileRegistry
- type ProviderRef
- type RecyclePolicy
- type Resource
- type ResourceCollection
- type ResourceID
- type ResourceProfile
- type ResourceRequest
- type ResourceState
- type ResourceType
- type RevokedAgentIdentity
- type Sandbox
- type SandboxID
- type SandboxPolicies
- type SandboxStatus
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.
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 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 ¶
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.
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 ¶
func (r *ProfileRegistry) Has(t ResourceType, name ResourceProfile) bool
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" 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" )