Skip to main content

Shared Engineering Standards Appendix

This appendix is the centralized reference for the engineering standards that all 7 case studies in this series follow. Each case study document repeats only the minimum necessary context β€” full field tables, capability lists, release checklists, etc. all point here, avoiding duplication that would drift out of sync.

Reading order tip: new readers should skim this appendix first to build global mental model, then dive into specific cases. Experienced readers can jump in as needed.

metadata.json / manifest.json Schema​

The NeoMind ecosystem has two types of publishable artifacts β€” extensions (Rust cdylib + optional React frontend) and dashboard components (pure React, distributed as bundle.js). The two metadata filenames differ:

  • Extension β†’ metadata.json (source: extensions/<id>/metadata.json, auto-generated from Cargo.toml by build scripts)
  • Component β†’ manifest.json (source: components/<id>/manifest.json, hand-maintained)

The two schemas overlap heavily, but extensions uniquely have builds / frontend / type and components uniquely have size_constraints / has_* family / default_config. The tables below merge them, grouped by purpose, with each field annotated by source.

Basic Information​

FieldTypeRequiredDescriptionExample
idstringyesUnique artifact identifier, globally unique. Extensions use kebab-case (hyphens, e.g. weather-forecast-v2); components use snake_case (underscores, e.g. ne101_camera)weather-forecast-v2 / ne101_camera
namestring | objectyesDisplay name. Components support { "en": "...", "zh": "..." } i18n object"weather forecast" / { "en": "NE101 Camera Panel", "zh": "NE101 ζ„ŸηŸ₯摄像倴青板" }
versionstring (semver)yesThree-segment semantic version. Extensions read automatically from Cargo.toml; components hand-written"2.7.6" / "2.14.9"
descriptionstring | objectyesOne-line description; components support i18n"Real-time weather forecast..."
authorstringyesAuthor or team name"NeoMind Team" / "CamThink Team"
licensestringext requiredSPDX license identifier"Apache-2.0" / "MIT"
homepagestring (URL)noSource code or documentation URL"https://github.com/camthink-ai/NeoMind-Extensions/tree/main/extensions/weather-forecast-v2"
iconstringno (common in components)Icon identifier, maps to NeoMind icon library"Camera"

Type & Categorization​

FieldTypeRequiredDescriptionExample
typestringext requiredArtifact type. Currently fixed at "native" (Rust cdylib); WASM type will be added in the future"native"
categoriesstring[]ext optionalMarketplace category tags array["weather"]
categorystringcomponent optionalSingle category, used by components"device"

Build Artifacts (Extension-only)​

The builds field is extension-only and lists download URLs for 5 cross-platform build artifacts. Each key corresponds to a Rust target triple, and the URL points to a GitHub Release asset.

{
"builds": {
"darwin-aarch64": { "url": "https://github.com/camthink-ai/NeoMind-Extensions/releases/download/v2.7.6/weather-forecast-v2-2.7.6-darwin_aarch64.nep" },
"darwin-x86_64": { "url": ".../weather-forecast-v2-2.7.6-darwin_x86_64.nep" },
"linux-x86_64": { "url": ".../weather-forecast-v2-2.7.6-linux_amd64.nep" },
"linux-aarch64": { "url": ".../weather-forecast-v2-2.7.6-linux_arm64.nep" },
"windows-x86_64": { "url": ".../weather-forecast-v2-2.7.6-windows_amd64.nep" }
}
}

The full list of 5 targets is described in Cross-Platform Build Target Matrix.

Frontend Declaration (Extension-only)​

Extensions with React frontend must declare the frontend object:

FieldTypeRequiredDescriptionExample
frontend.componentsstring[]yesComponent name array, plain strings not objects["WeatherCard"]
frontend.entrypointstringyesUMD entry filename, must match frontend.json's entrypoint"weather-forecast-v2-components.umd.cjs"

Common mistake: writing components as an object array [{ "name": "WeatherCard", ... }]. The marketplace parser will reject it.

Component-only Fields (manifest.json exclusive)​

FieldTypeRequiredDescriptionExample
size_constraintsobjectyesGrid size constraints (unit: grid cells){ "min_w": 2, "min_h": 2, "default_w": 3, "default_h": 3, "max_w": 6, "max_h": 6 }
has_data_sourcebooleanyesWhether Data Source tab is supportedfalse
has_device_bindingbooleanyesWhether device binding is supported (affects deviceContext prop injection)true
device_type_filterstring[]noRestricts bindable device types; empty means unrestricted["ne101_camera"]
has_display_configbooleanyesWhether Display Config tab is shownfalse
has_actionsbooleanyesWhether Actions tab is shown (buttons, commands)false
default_configobjectyesDefault config object used when user has not customizedsee code block below
global_namestringyesGlobal variable name that bundle.js mounts onto window"NE101CameraPanel"
export_namestringyesIIFE default export name, usually same as global_name"NE101CameraPanel"
max_data_sourcesintegerOptionalLimits the number of bindable data sources when has_data_source is true12

default_config example (excerpted from ne101_camera):

{
"default_config": {
"showMetrics": true,
"showCommands": true,
"location": "",
"displayTitle": "",
"processingEnabled": false,
"processingExtensionId": "",
"processingTemplate": "object_detection"
}
}

Capability Categories​

NeoMind implements fine-grained access control for extensions to platform features through explicit capability declaration. Extensions call platform capabilities via CapabilityContext::invoke_capability(name, params) in code; the runtime validates whether the called capability is within the extension's declared whitelist β€” undeclared direct calls will panic (not degrade gracefully, forcing developers to explicitly request permissions).

Complete Capability Enumeration​

The table below lists all variants of SDK ExtensionCapability (source: neomind-extension-sdk):

Capability IdentifierMeaningTypical Extensions
device_metrics_readRead device metricsDashboard-type extensions
device_metrics_writeWrite device metrics (including virtual metrics)weather-forecast-v2, all bridge extensions
device_controlSend commands to deviceshomeassistant-bridge, modbus-bridge
storage_queryQuery time-series storageData analysis extensions
event_publishPublish eventsAutomation trigger extensions
event_subscribeSubscribe to eventsLinkage extensions
telemetry_historyQuery device telemetry historyTrend / history extensions
metrics_aggregateAggregate device metricsReporting extensions
extension_callCall other extensionsOrchestration extensions
agent_triggerTrigger AI AgentLLM-linked extensions
rule_triggerTrigger automation rulesAutomation extensions
device_template_registerRegister device type templateslorawan-bridge, modbus-bridge, onvif-bridge, bacnet-bridge, opcua-bridge, uink-rms-bridge
device_registerRegister device instancesAll bridge extensions
device_unregisterUnregister device instancesBridge extension cleanup logic
Custom(String)Custom capabilityProject-specific scenarios

Real-world Usage Patterns​

Based on grepping the repository source code, the most commonly used capabilities are device_metrics_write (used by nearly all bridge extensions to report telemetry) and device_register / device_template_register (bridge extensions registering external devices into the NeoMind device model). Typical invocation pattern:

use neomind_extension_sdk::capabilities::CapabilityContext;
use serde_json::json;

// Write virtual metric (most common)
let _ = ctx.invoke_capability("device_metrics_write", &json!({
"device_id": "virtual-sensor-1",
"metric": "temperature",
"value": 25.5
}));

// Register device type template (at bridge startup)
let result = ctx.invoke_capability("device_template_register", &template_json);

// Register device instance
let result = ctx.invoke_capability("device_register", &device_json);

Sync vs Async Invocation​

  • Async context (e.g., execute_command): use ctx.invoke_capability(name, params).await
  • Sync context (e.g., produce_metrics / handle_event): extensions internally wrap an invoke_capability_sync() method, bridged via CapabilityContext::default()

Three-Segment Version Consistency​

The NeoMind-Extensions repository has three tiers of version numbers that must all agree at release time (unless there is an explicit reason to differ):

FileVersion MeaningExample
VERSIONMarketplace release version (repo-level single version)2.7.0
extensions/index.json β†’ versionMarketplace release version (synced with VERSION)2.7.0
extensions/*/Cargo.toml β†’ versionPer-extension version (affects package filename)2.7.0
extensions/*/metadata.json β†’ versionAuto-read from Cargo.toml, never hand-written2.7.0

Common Mistake​

Only updating VERSION and index.json, but forgetting to update each extension's Cargo.toml. Consequences:

  • Package filename uses old version: weather-forecast-v2-2.6.0-darwin_aarch64.nep
  • GitHub Release title says v2.7.0, but the packages inside are 2.6.0
  • index.json builds URLs point to 2.7.0 asset names, but actual filenames are 2.6.0 β†’ 404
  • User experience confusion, marketplace install failure

Correct Workflow​

Use ./scripts/update-versions.sh to sync in one step:

# Full update: sync Cargo.toml + VERSION + generate JSON (recommended)
./scripts/update-versions.sh 2.7.0 --bump-extensions

# Verify version consistency (must pass!)
./scripts/update-versions.sh 2.7.0 --check

Cross-Platform Build Target Matrix​

NeoMind extensions support 5 targets (not 6 β€” there is no windows-aarch64):

Target KeyRust Target TripleArtifact SuffixUse Case
darwin-aarch64aarch64-apple-darwin.dylibApple Silicon macOS (M1/M2/M3/M4)
darwin-x86_64x86_64-apple-darwin.dylibIntel macOS
linux-x86_64x86_64-unknown-linux-gnu.soGeneral-purpose Linux servers
linux-aarch64aarch64-unknown-linux-gnu.soARM Linux (Raspberry Pi 4/5, ARM servers)
windows-x86_64x86_64-pc-windows-msvc.dllWindows 10/11

Build Command​

# Build .nep packages for all 5 targets in one shot
./build.sh --release 2.7.0

# Build a single extension only
./build.sh --single weather-forecast-v2 --release 2.7.0

build.sh internally uses cross (Docker-based) or the local toolchain for cross-compilation. Developers who have the corresponding Rust target toolchains installed locally can skip Docker and compile directly.

.nep Package Structure​

weather-forecast-v2-2.7.6-darwin_aarch64.nep   (ZIP format)
β”œβ”€β”€ manifest.json # Install manifest (converted from metadata.json)
β”œβ”€β”€ binaries/
β”‚ └── darwin_aarch64/
β”‚ └── libneomind_extension_weather_forecast_v2.dylib
β”œβ”€β”€ frontend/
β”‚ └── weather-forecast-v2-components.umd.cjs
└── models/ # Optional: ONNX models
└── model.onnx

Test Coverage & Quality Requirements​

The NeoMind ecosystem has explicit testing requirements β€” extensions that fail are not released.

Extension Tests (Rust)​

Test TypeLocationRequirementReference
Unit testssrc/lib.rs inside #[cfg(test)] mod testsAt least cover happy path of core commandsweather-forecast-v2/src/lib.rs
Integration teststests/ directoryAt least 1 integration test fileweather-forecast-v2/tests/

Minimal example:

// src/lib.rs
#[cfg(test)]
mod tests {
use super::*;

#[tokio::test]
async fn test_increment_command() {
let ext = MyExtension::new();
let result = ext.execute_command("increment", &json!({"amount": 5}))
.await
.unwrap();
assert_eq!(result["counter"], 5);
}
}

Component Tests (JavaScript)​

NeoMind-Dashboard-Components uses hand-written IIFE as the distribution format; tests verify the IIFE export by mocking the window global:

Test TypeLocationRequirementReference
Bundle test<component>/test_bundle.jsEvery component must have onene101_camera/test_bundle.js

Typical test_bundle.js structure:

// 1. Mock window globals
global.window = {
React: require('react'),
jsxRuntime: { jsx: () => null, jsxs: () => null },
};

// 2. Load bundle.js (IIFE mounts onto window.<global_name>)
require('./bundle.js');

// 3. Verify export
const Component = window.NE101CameraPanel;
if (typeof Component !== 'function') {
throw new Error('NE101CameraPanel not exported correctly');
}

console.log('βœ“ bundle.js export test passed');

CI Requirements​

  • Extensions repo: cargo test --workspace must be green to release
  • Components repo: every component's test_bundle.js must pass when run with node
  • Both repos have GitHub Actions that automatically run tests on PRs

Release Checklist​

Before releasing a new version, confirm each item:

  • All Cargo.toml version numbers agree (./scripts/update-versions.sh $VERSION --check passes)
  • extensions/index.json version field updated
  • VERSION file updated
  • cargo test --workspace is green
  • ./build.sh --release $VERSION produces .nep packages for all 5 targets
  • Verify dist/*.nep filenames have consistent version (ls dist/*.nep)
  • Components: bundle.js + manifest.json synced to NeoMind-Dashboard-Components repo
  • GitHub Release created, 5 .nep files uploaded to release assets
  • Case studies 0-overview.md version alignment table audit date updated

Full Release Workflow​

VERSION=2.7.0

# Step 1: Sync versions + generate JSON
./scripts/update-versions.sh $VERSION --bump-extensions

# Step 2: Verify consistency
./scripts/update-versions.sh $VERSION --check

# Step 3: Commit version bump
git add . && git commit -m "chore: bump to v$VERSION"

# Step 4: Build and package
./build.sh --release $VERSION

# Step 5: Verify package filenames have consistent version
ls dist/*.nep

# Step 6: Tag and release
git tag v$VERSION
git push origin main --tags
gh release create v$VERSION ./dist/*.nep --title "v$VERSION"

Security Requirements​

NeoMind enforces strict security constraints on extensions and components. Violating any item will be rejected in code review.

unsafe Rust​

  • Avoid unsafe blocks whenever possible
  • If unavoidable (e.g., FFI bindings, performance-critical paths), the PR description must explicitly state:
    • Why unsafe is required
    • How memory safety is guaranteed
    • Whether a safe wrapper is provided

Explicit Capability Declaration​

Extensions declare required capabilities in metadata.json; runtime enforces validation:

  • Calling device_metrics_write without declaring it β†’ panic (not graceful degradation)
  • This is intentional: forces developers to explicitly request permissions, avoiding "silent failure"
  • Bridge extensions need device_template_register + device_register + device_metrics_write at startup

Process Isolation​

All extensions run in separate processes:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ NeoMind Main Process β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ UnifiedExtensionService β”‚ β”‚
β”‚ β”‚ IPC via stdin/stdout β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ FFI / IPC
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Extension Runner Process β”‚
β”‚ (one per extension, isolated) β”‚
β”‚ - Native: .dylib / .so / .dll β”‚
β”‚ - WASM: wasmtime runtime β”‚
β”‚ - Crashes do not affect main β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Benefits:

  • Crash isolation: extension panics cannot take down the main process
  • Memory isolation: each extension has its own address space
  • Resource limits: CPU / memory can be capped per extension
  • Independent lifecycle: extensions can be restarted individually

Frontend Component Sandbox​

A dashboard component's bundle.js gets runtime dependencies injected via window and does not directly access:

  • File system
  • Network (fetch / XMLHttpRequest are wrapped by Host)
  • Native APIs
// Inside bundle.js, dependencies are injected via window (not bundled)
var React = window.React;
var jsx = window.jsxRuntime.jsx;
var jsxs = window.jsxRuntime.jsxs;

This ensures components can run on any Host environment (Tauri desktop, web browser, embedded WebView), with the Host deciding which capabilities to expose.

Panic Configuration​

Extension Cargo.toml must set:

[profile.release]
panic = "unwind" # REQUIRED! "abort" will crash the host process on any panic
opt-level = 3
lto = "thin"

Further Reading​


Last updated: 2026-06-22