Controller Foundation PRD#

Status#

Draft for the first development milestone of cleanroom-controller.

Summary#

The Controller Foundation Milestone establishes the controller as the operator interface and API authority for Cleanroom node inventory, power control, and boot configuration foundations.

This milestone is not intended to produce the final Cleanroom product. It should produce a simple but structurally sound controller that can manage real hardware power operations, record node and provider inventory, expose those operations through PocketBase-backed APIs, and define the boot-image and boot-profile model that later PXE/iPXE work will use.

Documentation Authority#

cleanroom-docs is the sole documentation location for product usage, development architecture, operator workflows, and product requirements.

Other repositories may keep short README files for local build commands or repo orientation, but durable decisions, usage docs, architecture docs, and PRDs belong in cleanroom-docs.

Users#

The first users are Cleanroom operators and debuggers.

The milestone should optimize for the person standing up and troubleshooting the lab, not for a polished general-audience product. It should make the controller useful for real node management while keeping the architecture ready for future UI, MCP, worker, scheduling, and boot orchestration work.

Goals#

  • Run a PocketBase-backed controller through the serve subcommand.
  • Use PocketBase for the web backend, API, persistence layer, and admin UI.
  • Provide a configuration framework that separates startup configuration from runtime operator-managed configuration.
  • Let operators configure nodes, node groups, power providers, power targets, boot images, and boot profiles through the PocketBase UI/API.
  • Let CLI commands call the controller HTTP API without prompting the operator for authentication during normal use.
  • Support queued power operations for nodes and node groups.
  • Implement the first real power provider for Junos PoE switch control.
  • Design provider extensibility for PoE Switch, Home Assistant, and Manual power providers.
  • Track desired power state, observed power state, observation time, confidence, operation history, provider responses, and errors.
  • Define the boot image and boot profile concepts needed for later PXE/iPXE work.
  • Keep job scheduling, worker execution, MCP integration, and artifact storage out of the first milestone.

Milestone Definition#

The Controller Foundation Milestone is complete when an operator can:

  1. Start the controller with the serve subcommand.
  2. Open the PocketBase UI.
  3. Configure a Junos PoE switch power provider.
  4. Create nodes and node groups.
  5. Map nodes to provider-specific power targets.
  6. Request power on, power off, and power cycle operations for a node.
  7. Request power operations for a manually selected group of nodes.
  8. See queued, running, completed, failed, and awaiting-manual power operations.
  9. See desired power state, observed power state, last observed time, and errors.
  10. Review operation history and audit details.
  11. Define boot images and boot profiles, even if full PXE/iPXE serving is implemented in a later milestone.

Product Nouns#

Node#

A node is a physical or virtual host managed by the controller.

Each node has:

  • an immutable controller UUID
  • one or more hardware identifiers, including a PXE MAC address when available
  • a human-readable name
  • optional human labels such as node-0
  • provider-specific power target mapping
  • desired power state
  • observed power state
  • last observed time
  • optional boot profile assignment

The controller UUID is the durable primary identity. The PXE MAC is a hardware identity and boot matching key, but it should not be the only durable identity.

Node Group#

A node group is a manual collection of nodes.

Groups exist from the beginning so operators can model concepts such as rack, hardware class, test pool, or firmware cohort. Rule-based groups are a future idea and are out of scope for this milestone.

Groups may have boot profile assignments. A node may belong to more than one group, but boot precedence must remain deterministic.

Power Provider#

A power provider represents a system capable of changing or observing power state.

Initial provider categories:

  • PoE Switch
  • Home Assistant
  • Manual

Only the Junos PoE switch implementation is required for this milestone, but the architecture must not assume that all nodes are PoE powered or switch backed.

Power Target#

A power target maps a node to provider-specific addressing.

Examples:

  • Junos PoE switch interface ge-0/0/0
  • Home Assistant entity ID
  • manual instruction text for a human operator

Scheduler, boot, and operator workflows should operate at the node or group level. Provider-specific details should remain below the power abstraction.

Power Operation#

A power operation is a queued request to change or verify power state.

Supported intents:

  • power on
  • power off
  • power cycle
  • observe or refresh state

Power cycle is a first-class intent, not a general workflow engine. Providers own the provider-specific steps required to satisfy that intent. For Junos PoE, that may mean serialized off, delay, and on steps. This keeps the product simple without forcing operators to hand-write common reset behavior.

Boot Image#

A boot image is a controller-managed bootable artifact or artifact set.

The controller should manage boot image storage. It should not rely on external Buildroot output directories as durable references.

Boot Profile#

A boot profile is boot policy.

It describes how a node or group should boot, including image selection and eventual boot instructions such as kernel arguments, serial console parameters, default mode, rescue mode, fallback behavior, and iPXE script generation.

Boot image answers “what bits can be booted?” Boot profile answers “what should this node do when it boots?”

Configuration Model#

The controller should not attempt to force all configuration into a single YAML or JSON file.

Configuration has two classes:

Startup Configuration#

Startup configuration is required before PocketBase is available.

Examples:

  • listen address
  • listen port
  • data directory
  • bootstrap or setup mode settings
  • log level
  • log destination
  • TLS settings, if added

Startup configuration may come from file, environment variables, and command flags. All startup configuration keys, defaults, and precedence rules must be documented.

Runtime Configuration#

Runtime configuration is operator-managed state stored in PocketBase.

Examples:

  • nodes
  • node groups
  • power providers
  • power targets
  • provider credentials and secrets
  • default power operation timeouts
  • default power cycle delay
  • boot images
  • boot profiles
  • boot profile assignments

Runtime configuration should be editable through the PocketBase UI/API during this milestone. A purpose-built custom UI or SPA is deferred.

Configuration Documentation#

Every startup setting and runtime configuration model must be documented in cleanroom-docs.

This documentation should describe purpose, storage location, default behavior, validation expectations, and operational consequences.

API and CLI Model#

The controller is server first.

The serve subcommand runs PocketBase and the controller backend. CLI commands should call the HTTP API rather than implementing separate local business logic.

CLI authentication must not interrupt normal operator workflows. The exact mechanism may be no-auth for local development, PocketBase auth, a setup token, or another simple special-auth path. The milestone requirement is that CLI use does not repeatedly prompt the operator.

The API should expose generic node-level and group-level operations. Operators and scripts should not need to understand provider internals for common actions.

Power Architecture#

Power control is the first real hardware feature.

The architecture must support provider-specific implementations behind a common contract. The first implementation is Junos PoE switch control.

Providers differ in concurrency and state capabilities:

  • Junos PoE changes should be serialized per provider because switch configuration commits are not freely parallel.
  • Home Assistant may eventually support more parallel operations.
  • Manual provider operations may wait for a human and may optionally notify a human through a future chat or messaging integration.

The controller should use a generic power operation model while allowing each provider runner to define its own concurrency and execution behavior.

Power State#

The product should track real observed power state, not only requested state.

Each node should expose:

  • desired power state
  • observed power state
  • last observed time
  • confidence or freshness information
  • last observation error, if any

The controller should support both periodic polling and on-demand refresh when a provider can report state.

Manual Power#

Manual power is a first-class provider category.

Manual operations may enter an awaiting-human state such as awaiting-manual-power-on. Optional future notifications may ask a human administrator to perform the action.

Manual confirmation should not be the only completion path. If the controller observes that the node has come online or reached an expected state, it should be able to complete or advance the operation.

Audit and History#

Power operation history is required from the beginning.

Each operation should record:

  • requested action
  • requesting user or system
  • target node or group
  • provider
  • provider target
  • timestamps
  • queued, running, terminal, and manual-wait states
  • provider responses
  • observed state transitions
  • error text

Boot Architecture#

The controller should eventually be the PXE next-server and own everything needed for PXE/iPXE booting except the DHCP server itself.

The preferred long-term direction is iPXE because it enables node-specific boot parameters, richer boot menus, HTTP-served artifacts, rescue paths, and dynamic boot scripts.

This milestone should design the boot image and boot profile model, but full PXE/iPXE implementation may be phased after power and inventory are working.

Boot Assignment#

Boot profiles may be assigned globally, to groups, or to individual nodes.

Precedence:

  1. explicit node boot profile
  2. group boot profile
  3. global default boot profile

If multiple group assignments could apply, the controller must define a deterministic resolution rule before implementing group boot assignment.

User Interface Strategy#

The first milestone should use PocketBase UI and API as much as possible.

A purpose-built SPA or custom web UI is not required for the milestone. The eventual product may serve a custom UI from the PocketBase-backed controller, but the first milestone should focus on good data models, API behavior, and real hardware operations.

A visible Settings or Configuration area should exist conceptually in the product model, but it may be represented by PocketBase collections and admin views during this milestone.

Authentication#

PocketBase admin/auth is sufficient for this milestone.

Authentication hardening is deferred. The PRD intentionally does not require a complete production auth model, role system, external identity provider, or secrets manager integration.

Provider secrets may be stored in PocketBase for now. External secret stores such as 1Password, AWS Secrets Manager, or similar systems are future extensions.

In Scope#

  • PocketBase-backed serve command.
  • Startup configuration framework and documentation.
  • Runtime configuration stored in PocketBase.
  • PocketBase collections for milestone product nouns.
  • Node and node group inventory.
  • Power provider inventory.
  • Power target mapping.
  • Queued power operations.
  • Junos PoE switch provider implementation.
  • Provider concurrency model.
  • Desired and observed power state tracking.
  • Operation audit and history.
  • Manual power provider design.
  • Boot image and boot profile data model.
  • Boot assignment precedence.
  • CLI commands that call the HTTP API for milestone operations.

Out Of Scope#

  • Job scheduling.
  • Worker execution.
  • MCP server implementation.
  • Artifact storage.
  • Log streaming.
  • Custom SPA or polished web UI.
  • Production auth hardening.
  • External secret store integration.
  • Rule-based node groups.
  • Full PXE/iPXE serving implementation, unless needed to validate the model.
  • General DAG or workflow engine.

Future Ideas#

  • Custom controller UI served by PocketBase.
  • iPXE dynamic script generation.
  • Node-specific boot menus.
  • Rescue boot profiles.
  • Boot profile fallback chains.
  • Home Assistant power provider.
  • Manual provider notifications through chat, email, Matrix, Slack, MCP, or other operator channels.
  • Rule-based node groups.
  • External secret stores.
  • MCP integration for AI-requested cleanroom environments.
  • Job scheduling and clean worker lifecycle management.
  • Artifact and log storage.

Explicit Guardrails#

  • Do not make the CLI a separate business-logic implementation. The CLI should call the controller API.
  • Do not design milestone one around job scheduling.
  • Do not build a general DAG or workflow engine for power operations.
  • Do not make provider-specific power details leak into normal node and group operations.
  • Do not treat external build output directories as durable boot image storage.
  • Do not make MAC address the only durable node identity.
  • Do not optimize for a polished public UI before the core models and hardware operations work.