Skip to main content

ADR-0002 Power-Native AgentOS

Architecture Decision Records

ADR-0002 Power-Native AgentOS

Context

Decision Timing

EnerOS was initiated in early 2024, positioned as a native AgentOS for the power and energy domain. At inception, the team had to answer a fundamental question: which layer of the system should hold the power system domain knowledge. This decision would determine the entire project’s technical form, team capability requirements, and ecosystem boundaries, and was difficult to reverse once released.

Current Pain Points

Traditional power software (such as commercial EMS, DMS, and SCADA upper-layer applications) places grid topology, load flow calculation, security constraints, equipment models, and other knowledge at the application layer, with each application modeling independently or calling third-party libraries. This paradigm exposes three fundamental problems in the AI Agent era:

Problem 1: Fragmented Grid Worldview

Dispatch applications, self-healing applications, and trading applications each maintain their own grid topology copies, with states out of sync:

// Typical pain point: three applications each hold a topology copy, states may be inconsistent
struct DispatchApp {
    topology: Topology,  // Copy A: switch state may lag
}

struct SelfHealingApp {
    topology: Topology,  // Copy B: just received switch change
}

struct TradingApp {
    topology: Topology,  // Copy C: based on schedule values, not reflecting real-time
}

When Agents need to collaborate across applications, they must first synchronize topology state, otherwise incidents like “dispatch Agent issues commands based on outdated topology” occur.

Problem 2: Optional Constraint Validation

Physical constraints (voltage limits, branch overloads, frequency deviations) depend on application-level voluntary calls; omissions lead to incidents:

// Anti-pattern: constraint validation scattered everywhere, relying on engineer diligence
fn dispatch_load(plant_id: &str, mw: f64) {
    set_output(plant_id, mw);  // Direct dispatch, no validation
    // Should call check_voltage_limit() / check_branch_overload() here
    // But newcomers easily miss it
}

Historical incident case: In 2019, a provincial grid omitted voltage upper limit validation in its self-healing application, causing bus overvoltage during power restoration, tripping 2 main transformers.

Problem 3: Redundant Modeling

Each new application must reimplement topology parsing, load flow solving, and equipment models, with development cycles of 6-12 months and uneven quality.

Specific Scenario Triggering the Decision

In January 2024, while developing the “Fault Self-Healing Agent” prototype, the team found it needed to access topology, load flow, and constraint capabilities simultaneously. Following the traditional path, they would either introduce 3 independent libraries (bringing dependency conflicts and state synchronization issues) or rewrite at the application layer (reinventing the wheel). This development experience directly triggered this ADR.

Constraints at Decision Time

ConstraintDescription
PerformanceTopology analysis < 1ms, load flow calculation < 12ms (IEEE 14-bus)
SecurityPhysical constraints must be 100% enforced, not bypassable
ComplianceMust satisfy MLPS 2.0 Level 3 and IEC 62443 SL-3
TeamCore team of 8, must deliver MVP within 6 months
EcosystemMust support IEC 61970/61968/61850 standard protocols
EvolutionNo rewrite within 5 years; new scenarios integrated through extension, not refactoring

Decision

Sink grid topology, load flow solving, constraint engine, equipment model library, and time-series storage as kernel-native first-class citizens, building a Power-Native AgentOS. All Agents access the grid view through unified kernel interfaces, with physical constraints enforced by the kernel rather than as optional checks.

Core Rationale

Reason 1: Single Grid Worldview

The kernel maintains the唯一 authoritative grid topology and state; all Agents access through system calls, eliminating state fragmentation at the architectural level:

// Kernel mode: sole authoritative topology
pub struct TopologyRegistry {
    inner: RwLock<TopologyGraph>,
    subscribers: Vec<SubscriberHandle>,
}

impl TopologyRegistry {
    /// Agent obtains topology snapshot via system call, ensuring consistency
    pub fn snapshot(&self) -> TopologySnapshot {
        let graph = self.inner.read();
        TopologySnapshot::from_graph(&graph)
    }

    /// Topology changes broadcast to all subscribers via events
    pub fn apply_switch_change(&self, switch_id: &str, state: SwitchState) -> Result<()> {
        let mut graph = self.inner.write();
        graph.update_switch(switch_id, state)?;
        let event = TopologyEvent::SwitchChanged { switch_id, state };
        self.broadcast(event)?;
        Ok(())
    }
}

// Agent side: access via system calls, no need to maintain own copy
fn agent_handle_fault(fault: FaultEvent) -> Result<IsolationPlan> {
    let topology = sys::topology_snapshot()?;  // System call
    let powerflow = sys::powerflow_solve(&topology, LoadCase::Current)?;
    let plan = isolation_planner::plan(&topology, &fault, &powerflow)?;
    sys::constraint_validate(&plan)?;  // Kernel-enforced validation
    Ok(plan)
}

Reason 2: Constraint-as-Law

Constraint validation is enforced in kernel mode; any operation violating constraints is rejected at the system call stage and cannot be bypassed:

pub struct ConstraintEngine {
    rules: Vec<ConstraintRule>,
}

impl ConstraintEngine {
    /// All remote commands must pass through this function; constraint violations return Err directly
    pub fn validate(&self, cmd: &ControlCommand) -> Result<ValidatedCommand> {
        for rule in &self.rules {
            rule.check(cmd)?;
        }
        Ok(ValidatedCommand::from(cmd))
    }
}

/// System call: dispatch control command
pub fn sys_control(cmd: ControlCommand) -> Result<()> {
    let validated = CONSTRAINT_ENGINE.validate(&cmd)?;  // Cannot be bypassed
    DEVICE_BUS.dispatch(validated)
}

Reason 3: Reuse Rather Than Reduplicate

New power applications need not duplicate modeling; development cycles drop from 6-12 months to 2-4 weeks.

Reason 4: Audit Consistency

All Agent operations pass through unified kernel audit points, enabling complete traceability for compliance forensics.

Reason 5: Performance Optimization Space

After sinking load flow and constraints to the kernel, zero-copy, shared memory, and other techniques can significantly reduce cross-process overhead; measured constraint validation < 10μs.

Five Pillars of Power-Native

PillarKernel ComponentCrateResponsibility
TopologyTopologyRegistryeneros-topologyGrid structure, switch state, connectivity analysis
Load FlowPowerflowSolvereneros-powerflowNewton-Raphson, P-Q decoupling, DC power flow
ConstraintConstraintEngineeneros-constraintVoltage, current, frequency, thermal stability constraints
EquipmentEquipmentLibraryeneros-equipmentUnified models for transformers, lines, switches, loads
Time-seriesTimeseriesEngineeneros-timeseriesNanosecond time-series data write and query

Agent-as-Grid-Node Abstraction

Each Agent is modeled as a node in the grid within the kernel, with clear electrical location and permission boundaries:

pub struct AgentNode {
    pub agent_id: AgentId,
    pub electrical_location: ElectricalLocation,  // Belonging bus/feeder
    pub jurisdiction: Jurisdiction,                // Jurisdiction scope
    pub permissions: PermissionSet,                // Permission set
}

impl AgentNode {
    /// Agent can only operate devices within its jurisdiction
    pub fn can_control(&self, device_id: &DeviceId) -> bool {
        self.jurisdiction.contains(device_id) && self.permissions.can_control(device_id)
    }
}

Consequences

Benefits

1. All Agents share a single grid worldview, avoiding state fragmentation

Measured in multi-Agent collaborative fault self-healing scenarios, topology synchronization latency dropped from 50-200ms at the application layer to < 1ms at the kernel level.

2. Physical constraints enforced, security boundary cannot be bypassed

Constraint validation is enforced in kernel mode; no Agent can bypass it. The system call layer provides audit points; all limit violations are recorded.

3. New power applications need not duplicate modeling, reducing integration cost

The first “Load Forecasting Agent” based on EnerOS took only 18 days from inception to launch, with power domain code accounting for < 5%.

4. Significant performance improvement

Constraint validation dropped from 1-5ms at the application layer to < 10μs at the kernel level, a 100-500x improvement.

5. Unified audit and compliance

All Agent operations pass through unified kernel audit points, satisfying MLPS 2.0 Level 3 audit requirements in one pass.

Costs

1. Larger kernel API surface area, heavier backward compatibility commitment

The kernel must stably maintain 5 types of first-class citizen interfaces (topology, load flow, constraints, equipment, time-series); each interface’s breaking changes require a full deprecation process.

2. Non-power domains require adapter layers, incurring abstraction tax

When general IoT scenarios use EnerOS, eneros-adapter-iot must map non-power devices to the grid model, with approximately 15% performance overhead.

3. High team capability requirements

Kernel developers must be proficient in Rust, power systems, and operating system principles simultaneously; recruiting and training costs are significant.

4. Complex test matrix

Each kernel change requires regression testing of 56 crates; CI pipeline runtime increased from 5 minutes to 28 minutes.

Follow-up Work

  • Introduce Agent-as-Grid-Node abstraction, making Agents grid nodes (landed in v0.20)
  • Implement Constraint-as-Law enforcement mechanism (landed in v0.25)
  • Subsequent ADR-0010 introduces Agent intelligence evolution on this basis
  • Design adapter layer interfaces for non-power domains (planned for v0.50)
  • Optimize CI pipeline with incremental testing and distributed builds

Alternatives

Option A: Standalone eneros-power-lib Library

Description: Encapsulate topology, load flow, constraints, and equipment models as a standalone Rust crate, used as an application-layer dependency for applications to call as needed.

Advantages:

  • Simple implementation; team can deliver MVP in 2 months
  • Applications freely choose whether to use, high flexibility
  • No kernel modification, minimal impact on system stability

Rejection Reasons:

  • Constraint validation becomes optional, cannot guarantee enforcement, violating the “security boundary cannot be bypassed” hard requirement
  • Applications still need to manage their own topology copies; state fragmentation unresolved
  • Cannot implement Agent-as-Grid-Node abstraction; Agents cannot be perceived by the kernel
  • Audit points scattered across applications; compliance forensics difficult

Option B: Linux User-space Middleware

Description: Build a resident process in Linux user space (similar to systemd daemon) providing power domain services, with applications calling via IPC.

Advantages:

  • No need to write kernel modules; low development difficulty
  • Can use mature power libraries from the C/C++ ecosystem (such as MATPOWER, Pylon)
  • Good fault isolation; middleware crashes do not affect the kernel

Rejection Reasons:

  • High IPC overhead; constraint validation latency rises from < 10μs to 1-5ms, failing to meet real-time domain requirements
  • Still treats power knowledge as “callable service” rather than “native abstraction”; Agent-as-Grid-Node cannot be implemented
  • Introduces single point of failure; middleware crash disables all Agents network-wide
  • Cannot leverage kernel optimization techniques like zero-copy

Option C: Extend Existing SCADA/EMS Platforms

Description: Extend Agent capabilities through plugin mechanisms on mature commercial SCADA/EMS platforms (such as OPEN DSS, PSS/E).

Advantages:

  • Mature power domain functionality, fully validated
  • Ready-made GUI and operations tools
  • Fast short-term launch

Rejection Reasons:

  • These platforms are closed-source or primarily C/C++/Python, failing to meet Rust memory safety requirements
  • Architecture is single-process applications, cannot support Agent multi-tenancy and isolation
  • Performance cannot meet real-time domain microsecond response
  • License and commercial terms restricted, unfavorable for open-source ecosystem

Option D: Kernel Module (Linux LKM)

Description: Implement power domain knowledge as Linux kernel modules, loaded into kernel space as .ko files.

Advantages:

  • Best performance; constraint validation can be as low as 1μs
  • True kernel-mode enforcement

Rejection Reasons:

  • Linux kernel modules must be developed in C, cannot use Rust’s memory safety guarantees (as of 2024, Rust for Linux is not yet stable)
  • Kernel module bugs cause entire system crashes; unacceptable for power systems
  • Kernel-mode debugging is difficult; low development efficiency
  • Difficult to cross-platform (Windows, embedded RTOS)

Impact Assessment

Impact on Architecture

This decision established EnerOS’s core architecture form: dual-domain kernel (general domain + real-time domain), with power domain knowledge in the general domain and the real-time domain responsible for protection and control. All subsequent ADRs build on this architecture.

Impact on Team

  • Need to recruit 2-3 Rust engineers with power system backgrounds
  • Existing application engineers need to learn kernel interfaces rather than self-modeling
  • Establish a “Power Kernel Group” as a long-term maintenance team

Impact on Ecosystem

  • Third-party power application developers only need to interface with kernel APIs, lowering entry barriers
  • Equipment vendors can connect through unified equipment model libraries, without adapting to multiple applications
  • Academic research can quickly validate algorithms based on kernel interfaces

Impact on Performance

MetricApplication-layer approachPower-Native approachImprovement
Topology analysis (1000 nodes)15ms< 1ms15×
Load flow calculation (IEEE 14)45ms< 12ms3.75×
Constraint validation (single)1.2ms< 10μs120×
Agent collaborative sync50-200ms< 1ms50-200×

Migration Path

For existing power applications migrating to EnerOS, the following path is recommended:

Phase 1 (1-2 weeks): Assessment
  ├─ Survey power domain functions used by the application
  ├─ Identify differences between self-built models and kernel models
  └─ Develop migration plan

Phase 2 (2-4 weeks): Adaptation
  ├─ Introduce eneros-topology to replace self-built topology
  ├─ Introduce eneros-powerflow to replace self-built load flow
  └─ Keep business logic unchanged through adapter layer

Phase 3 (1-2 weeks): Constraint Integration
  ├─ Remove application-layer constraint validation
  ├─ Connect to kernel ConstraintEngine
  └─ Verify complete constraint coverage

Phase 4 (ongoing): Optimization
  ├─ Leverage kernel zero-copy for performance optimization
  ├─ Connect to event bus for reactive implementation
  └─ Gradually migrate to Agent-as-Grid-Node model

Migration example code:

// Before migration: application-layer self-built topology and constraints
fn legacy_dispatch(plant_id: &str, mw: f64) {
    let topo = self.local_topology.lock();  // Self-built copy
    let flow = my_powerflow_solve(&topo);   // Self-built load flow
    if flow.check_voltage(plant_id) < 1.05 {  // Self-built constraints
        set_output(plant_id, mw);
    }
}

// After migration: using Power-Native kernel interfaces
fn native_dispatch(plant_id: &str, mw: f64) -> Result<()> {
    let cmd = ControlCommand::new(plant_id, mw);
    sys::control(cmd)  // Kernel automatically completes topology, load flow, constraints, dispatch
}

References

  • ADR Overview — ADR index and writing guidelines
  • ADR-0010 — Agent intelligence evolution path
  • Power-Native First concept — Design philosophy in detail
  • Agent-as-Grid-Node concept — Agent as grid node
  • Constraint-as-Law concept — Constraint enforcement mechanism
  • Michael Nygard, “Documenting Architecture Decisions”, 2011
  • IEC 61970 / 61968 / 61850 power industry standards
  • Related crates: eneros-topology, eneros-powerflow, eneros-constraint, eneros-equipment, eneros-timeseries