Skip to main content

v0.27.0 Release Notes

EnerOS v0.27.0 Release Notes

Release Date: 2025-10-26 Codename: Comply Git Tag: v0.27.0 Support Status: Stable Total Crates: 71 (4 new) Test Cases: 9050+ (500+ new)

Overview

EnerOS v0.27.0 “Comply” is a compliance-framework-focused release that provides a compliance foundation for EnerOS deployment in the regulated power industry. The power industry is critical information infrastructure, and data processing and system operations are subject to strict legal and regulatory constraints: the EU’s General Data Protection Regulation (GDPR) governs personal data processing, China’s Personal Information Protection Law (PIPL) regulates the flow of personal information, and power regulators in various countries have explicit requirements for data retention and audit traceability. v0.27.0 builds compliance requirements into the kernel framework, making EnerOS compliant by design.

This release introduces five core capabilities: Compliance Framework, GDPR Compliance, PIPL Compliance, Data Retention Policy, and Audit Log. All data processing operations go through compliance policy validation, and all critical operations generate tamper-evident audit records.

In terms of design philosophy, v0.27.0 adheres to “compliance as code” — compliance requirements are defined as executable policies rather than relying on manual processes; “protection by default” — personal data is stored masked by default and requires explicit authorization to access plaintext; “tamper-evident” — audit logs use append-only writes with hash chain structure, making any tampering detectable.

Key Metrics

MetricValueDescription
Compliance policies120+Built-in policies
Audit log throughput80,000 entries/secSingle node
Audit log verification< 5msSingle hash chain
Data masking types15Phone/ID/name, etc.
New Crates4Compliance-related
New tests500+Includes compliance tests

New Features

1. Compliance Framework

Introduces the eneros-comply Crate, providing a unified compliance framework. Compliance policies are defined as rules, and all data processing operations go through the policy engine for validation.

Policy Definition

use eneros_comply::{ComplianceFramework, Policy, PolicyEngine};

let framework = ComplianceFramework::new()
    .engine(PolicyEngine::strict())
    .load_policies("policies/");

// Register policy
framework.register_policy(Policy::new("gdpr-art-6")
    .rule(Rule::require_consent(
        DataCategory::Personal,
        Purpose::Marketing,
    )))?;

framework.register_policy(Policy::new("pipl-art-13")
    .rule(Rule::require_notice(
        DataCategory::Personal,
        Action::Collect,
    )))?;

Compliance Validation

// Auto-validate before data processing
let action = DataAction::new()
    .data(DataCategory::Personal)
    .purpose(Purpose::Analytics)
    .subject(user_id);

match framework.check(&action).await {
    Ok(_) => process_data(action).await?,
    Err(violations) => {
        for v in violations {
            log::error!("Compliance violation: {} - {}", v.policy, v.reason);
        }
    }
}

Compliance Standard Coverage

StandardRegionMain RequirementsCoverage
GDPREUPersonal data protectionComplete
PIPLChinaPersonal information protectionComplete
CCPACaliforniaConsumer privacyPartial
NERC CIPNorth AmericaPower cybersecurityPartial
MLPS 2.0ChinaMulti-Level Protection SchemeComplete
ISO 27001GlobalInformation securityPartial

2. GDPR Compliance

Introduces the eneros-comply-gdpr Crate, providing GDPR compliance capabilities. Covers data subject rights, lawful basis for data processing, data protection impact assessment, and other requirements.

Data Subject Rights

use eneros_comply_gdpr::{GdprEngine, SubjectRight};

let gdpr = GdprEngine::new(&framework);

// Right of access (Article 15)
let data = gdpr.handle_request(SubjectRight::Access {
    subject_id: user_id,
}).await?;
// Returns a copy of all personal data for this user

// Right to erasure (Article 17)
gdpr.handle_request(SubjectRight::Erasure {
    subject_id: user_id,
    reason: "User initiated account deletion",
}).await?;
// Deletes all personal data, retaining legally required portions

// Right to data portability (Article 20)
let portable = gdpr.handle_request(SubjectRight::Portability {
    subject_id: user_id,
    format: ExportFormat::Json,
}).await?;
// Record user consent
gdpr.record_consent(Consent::new()
    .subject(user_id)
    .purpose(Purpose::LoadProfiling)
    .lawful_basis(LawfulBasis::Consent)
    .timestamp(now())
    .expiry(Duration::days(365)))?;

// Auto-delete related data after consent withdrawal
gdpr.withdraw_consent(user_id, Purpose::LoadProfiling).await?;

GDPR Article Coverage

ArticleRequirementImplementation
Art. 6Lawful basisConsent management
Art. 7Consent conditionsExplicit consent records
Art. 15Right of accessData export
Art. 17Right to erasureData erasure
Art. 20Right to portabilityStructured export
Art. 25Privacy by designDefault masking
Art. 30Records of processingProcessing logs
Art. 33Data breach notificationNotification mechanism

3. PIPL Compliance

Introduces the eneros-comply-pipl Crate, providing compliance capabilities for China’s Personal Information Protection Law.

Personal Information Classification

use eneros_comply_pipl::{PiplEngine, InfoCategory};

let pipl = PiplEngine::new(&framework);

// Data classification
let category = pipl.classify(&data)?;
match category {
    InfoCategory::General => { /* General personal information */ }
    InfoCategory::Sensitive => {
        // Sensitive personal information (ID, biometrics, etc.)
        // Requires separate consent
        pipl.require_separate_consent(&data)?;
    }
    InfoCategory::NonPersonal => { /* Non-personal information */ }
}

Cross-border Transfer

// Personal information outbound compliance
let transfer = CrossBorderTransfer::new()
    .data(&personal_data)
    .destination("US")
    .purpose("Technical support");

match pipl.check_cross_border(transfer).await {
    Ok(approval) => {
        // Passed security assessment or obtained certification
        pipl.execute_transfer(approval).await?;
    }
    Err(reason) => {
        log::warn!("Cross-border transfer rejected: {}", reason);
    }
}

PIPL Article Coverage

ArticleRequirementImplementation
Art. 13Processing basisLawful basis
Art. 17Notification obligationPrivacy notice
Art. 28Sensitive informationSeparate consent
Art. 38Cross-border transferSecurity assessment
Art. 47Retention periodRetention policy

4. Data Retention Policy

Introduces the eneros-comply-retention Crate, providing data retention policy management. Different types of data have different legal retention periods; expired data is automatically archived or deleted.

Retention Policy

use eneros_comply_retention::{RetentionEngine, RetentionPolicy};

let engine = RetentionEngine::new()
    .policy(RetentionPolicy::new("scada-data")
        .category(DataCategory::Operational)
        .retain_for(Duration::years(2))
        .action_after(RetentionAction::Archive))
    .policy(RetentionPolicy::new("audit-log")
        .category(DataCategory::Audit)
        .retain_for(Duration::years(6))  // MLPS 2.0 requirement
        .action_after(RetentionAction::Keep))
    .policy(RetentionPolicy::new("user-personal")
        .category(DataCategory::Personal)
        .retain_for(Duration::years(1))
        .action_after(RetentionAction::Delete)));

// Auto-execute retention policy
engine.schedule_daily().await?;

Data Lifecycle

// Mark data lifecycle
let data = DataRecord::new("load-profile-2025")
    .category(DataCategory::Operational)
    .created_at(now())
    .retention(Duration::years(2));

engine.track(data).await?;

// Query data status
let status = engine.status("load-profile-2025").await?;
match status {
    LifecycleStatus::Active => { /* Normal use */ }
    LifecycleStatus::Archived => { /* Archived */ }
    LifecycleStatus::PendingDeletion => { /* Pending deletion */ }
}

Retention Period Table

Data TypeRetention PeriodBasisExpiry Action
SCADA telemetry2 yearsIndustry standardArchive
Alarm records3 yearsMLPS 2.0Archive
Audit logs6 yearsMLPS 2.0Keep
Operation tickets5 yearsPower regulationsArchive
Personal information1 yearPIPLDelete
Fault recording1 yearIndustry standardDelete

5. Audit Log

Introduces the eneros-comply-audit Crate, providing tamper-evident audit logs. Uses a hash chain structure, making any tampering detectable.

Audit Records

use eneros_comply_audit::{AuditLog, AuditEvent};

let audit = AuditLog::new("audit.log")
    .hash_chain(HashChain::Sha256)
    .rotation(Rotation::daily());

// Record audit event
audit.record(AuditEvent::new()
    .actor(Actor::Agent("dispatch-1"))
    .action(Action::Execute(Command::OpenSwitch { id: 42 }))
    .resource(Resource::Switch(42))
    .result(Result::Success)
    .timestamp(now())
    .ip("10.0.1.5")).await?;

Log Verification

// Verify log integrity
let verification = audit.verify_chain(
    Range::from("2025-10-01").to("2025-10-26"),
).await?;

if verification.tampered_records.is_empty() {
    println!("Audit log intact, no tampering");
} else {
    for r in verification.tampered_records {
        println!("Tampered record: {} expected hash {} actual {}",
            r.id, r.expected_hash, r.actual_hash);
    }
}

Audit Event Types

Event TypeDescriptionRecorded Content
Authentication eventLogin/logoutUser, IP, time
Authorization eventPermission changeSubject, permission, operator
Operation eventSystem operationCommand, parameters, result
Data eventData accessData, operation, subject
Configuration eventConfiguration changeOld value, new value, operator

Improvements

  • Audit Performance: Audit log writes changed from synchronous to batch async, throughput improved 5x
  • Policy Cache: Compliance policy validation results cached by fingerprint; repeated validation reduced from 2ms to 0.1ms
  • Masking Algorithm: Added Format-Preserving Encryption (FPE) to maintain format after masking
  • Audit Query: Supports SQL-style audit log queries for compliance report generation

Bug Fixes

  • Fixed eneros-comply-gdpr not cascading cleanup of timeseries data during data deletion (#2708)
  • Fixed eneros-comply-pipl sensitive information classification missing biometrics (#2713)
  • Fixed eneros-comply-retention retention period calculation error across years (#2718)
  • Fixed eneros-comply-audit hash chain breakage during log rotation (#2723)

Breaking Changes

  • DataAction::new: Added required purpose: Purpose field
  • AuditEvent: Field user renamed to actor to support broader subject types

Upgrade Guide

  1. Run cargo update -p eneros-comply
  2. Add purpose field to all DataAction calls
  3. Update AuditEvent.user field access to actor
  4. Refer to docs/migration/v0.27.0.md for detailed migration steps