To facilitate AI-Driven Development and Human Code Review, all plugins in the ecosystem generally follow the "ObjectStack Plugin Standard" (OPS).
🎯 Goal: Ensure an AI agent can instantly understand the project structure and know exactly where to create or modify files without searching.
We recommend organizing code by Business Domain (Module) rather than technical file type. This keeps related logic (Schema, UI, Automation) co-located, fitting neatly into an AI's context window.
my-plugin/
├── package.json
├── objectstack.config.ts # Plugin Entry Point
├── src/
│ ├── main.ts # Logic Entry (Exports)
│ │
│ └── [module-name]/ # e.g., "project-management"
│ ├── [object].object.ts # Database Schema
│ ├── [object].trigger.ts # Backend Logic Hook
│ ├── [object].client.ts # Frontend Logic
│ ├── [object].view.ts # UI Layouts (Grid, Forms)
│ ├── [object].action.ts # Custom Buttons/Actions
│ ├── [process].flow.ts # Automation Flows
│ └── permissions.ts # Module-specific permissions
plugins/crm/
├── package.json
├── src/
│ ├── leads/
│ │ ├── lead.object.ts # "lead" Object definition
│ │ ├── lead.trigger.ts # "beforeInsert" logic
│ │ └── lead.view.ts # "All Leads" grid view
│ │
│ ├── sales/
│ │ ├── opportunity.object.ts
│ │ ├── opportunity.view.ts
│ │ └── quote.object.ts
│ │
│ └── analytics/
│ └── sales-dashboard.dashboard.ts
We use Semantic Suffixes to tell the AI exactly what a file contains.
Format: snake_case_name.SUFIX.ts
| Suffix | Purpose | Content Type |
|---|---|---|
*.object.ts |
Data Schema | Data.ObjectSchema (Zod) |
*.field.ts |
Field Extensions | Data.FieldSchema |
*.trigger.ts |
Backend Logic | Function Hooks (Before/After) |
*.app.ts |
App Definition | UI.AppSchema (Navigation) |
*.view.ts |
UI Views | UI.ViewSchema (Grid/Form) |
*.page.ts |
Custom UI | UI.PageSchema |
*.dashboard.ts |
Analytics | UI.DashboardSchema |
*.flow.ts |
Automation | Automation.FlowSchema |
*.router.ts |
Custom API | Express/Router definitions |
Ideally, define one primary resource per file.
- ✅
lead.object.tsexportsLeadObject. - ❌
crm.tsexportsLeadObject,ContactObject, andDealObject.
Why? It prevents huge files that get truncacted in AI context, and makes file-search reliable.
Always strictly type your exports using the spec definitions.
import { ObjectSchema } from '@objectstack/spec/data';
// ✅ GOOD: AI knows exactly what this is
export const ProjectObject: ObjectSchema = {
name: 'project',
fields: { ... }
};Each module folder should have an index.ts that exports its public artifacts. This allows the manifest loader to simply import the module.
// src/leads/index.ts
export * from './lead.object';
export * from './lead.trigger';
export * from './lead.view';To help AI understand the "intent" of a file, use a standard JSDoc header.
/**
* @domain CRM
* @object Lead
* @purpose Defines the structure of a Sales Lead and its status lifecycle.
*/
export const LeadObject = ...The microkernel architecture provides the following runtime capabilities for plugins. The Zod schemas governing each capability live in src/kernel/.
Hot reload supports development, staging, and production environments. The environment field controls the safety level:
| Environment | Behavior |
|---|---|
development |
Fast reload with file watchers, no health validation required |
staging |
Production-like reload with validation but relaxed rollback |
production |
Full safety: health validation, auto-rollback, connection draining |
Production safety features (productionSafety):
- Health validation — run health checks after reload before accepting traffic
- Rollback on failure — auto-rollback if reloaded plugin fails health check
- Connection draining — gracefully drain active requests before reloading
- Concurrency control — limit concurrent reloads (
maxConcurrentReloads) - Reload cooldown — minimum interval between reloads of the same plugin (≥1s)
Sandboxing supports configurable scope and isolation level:
| Scope | Description |
|---|---|
automation-only |
Sandbox automation/scripting plugins only (default) |
untrusted-only |
Sandbox plugins below a trust threshold |
all-plugins |
Sandbox all plugins for maximum isolation |
Isolation levels: none, process, vm, iframe, web-worker.
Inter-Plugin Communication (IPC): Isolated plugins communicate with the kernel and other plugins via configurable IPC:
- Transports:
message-port,unix-socket,tcp,memory - Configurable message size limit, timeout, and service ACL (
allowedServices)
Plugins can be loaded and unloaded at runtime without restarting the kernel:
DynamicLoadRequestSchema— Load a plugin fromnpm,local,url,registry, orgitsources with optional integrity verificationDynamicUnloadRequestSchema— Graceful/forceful/drain unload with dependency awareness (cascade,warn, orblockdependents)ActivationEventSchema— Lazy activation triggers:onCommand,onRoute,onObject,onEvent,onService,onSchedule,onStartupPluginDiscoveryConfigSchema— Runtime discovery from registries and local directories with polling and trust filteringDynamicLoadingConfigSchema— Subsystem configuration: max dynamic plugins, default sandbox policy, allowed sources, integrity requirements
| Capability | Status | Schema / Details |
|---|---|---|
| Plugin Registration | ✅ | manifest.zod.ts — objectstack.config.ts plugin array, ordered initialization |
| Lifecycle Hooks | ✅ | plugin.zod.ts — init() → start() → healthCheck() → destroy() |
| Service Registry | ✅ | service-registry.zod.ts — 17 services across 13 plugins via ctx.registerService() |
| Event Bus | ✅ | events.zod.ts — Pub/sub with pattern matching |
| Dependency Resolution | ✅ | plugin-loading.zod.ts — Declared dependencies with conflict resolution |
| Health Checks | ✅ | plugin-lifecycle-advanced.zod.ts — Per-plugin health + system aggregation |
| Hot Reload | ✅ | plugin-loading.zod.ts — Dev + production-safe with rollback and draining |
| Plugin Isolation | ✅ | plugin-loading.zod.ts — Configurable scope + IPC for process boundaries |
| Dynamic Loading | ✅ | plugin-runtime.zod.ts — Runtime load/unload with activation events and discovery |