| title | Kernel Services Checklist |
|---|---|
| description | Complete inventory of ObjectStack kernel services with protocol methods, implementation status, and development requirements. |
The ObjectStack protocol defines 17 kernel services registered via the CoreServiceName enum. Each service maps to a set of protocol methods (57 total), governed by the ObjectStackProtocol interface.
Key architecture principle: The kernel itself only provides data and metadata (framework). Everything else — including auth and automation — is delivered by plugins. The current @objectstack/objectql package is an example kernel implementation to get the basic API running; production kernels will be rebuilt as new plugins.
- ✅ Implemented — 18 protocol methods (kernel-provided)
⚠️ Framework — metadata (in-memory registry, DB persistence pending)- 🟡 In Development — auth (plugin structure complete, logic planned)
- ❌ Plugin Required — 38 protocol methods (to be delivered by plugins)
┌─────────────────────────────────────────────────────────┐
│ Kernel Layer │
│ ┌──────────────────┐ ┌──────────────────────────────┐ │
│ │ metadata (⚠️) │ │ data + analytics (✅) │ │
│ │ In-memory only │ │ ObjectQL example kernel │ │
│ │ DB persistence │ │ Will be rebuilt as plugins │ │
│ │ pending │ │ │ │
│ └──────────────────┘ └──────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ Plugin Layer │
│ All other services: auth, automation, workflow, ui, │
│ realtime, notification, ai, i18n, graphql, search, │
│ file-storage, cache, queue, job │
│ │
│ Discovery API reports: enabled/unavailable/degraded │
│ per service so clients adapt their UI accordingly │
└─────────────────────────────────────────────────────────┘
| # | Service Name | Criticality | Methods | Status | Provider |
|---|---|---|---|---|---|
| 1 | metadata | core |
7 | Kernel (in-memory) | |
| 2 | data | required |
9 | ✅ Implemented | @objectstack/objectql |
| 3 | analytics | optional |
2 | ✅ Implemented | @objectstack/objectql |
| 4 | auth | core |
— | 🟡 In Development | @objectstack/plugin-auth |
| 5 | ui | optional |
5 | ❌ Plugin Required | TBD plugin |
| 6 | workflow | optional |
3 | ❌ Plugin Required | TBD plugin |
| 7 | automation | optional |
1 | ❌ Plugin Required | TBD plugin |
| 8 | realtime | optional |
6 | ❌ Plugin Required | TBD plugin |
| 9 | notification | optional |
7 | ❌ Plugin Required | TBD plugin |
| 10 | ai | optional |
3 | ❌ Plugin Required | TBD plugin |
| 11 | i18n | core |
3 | ✅ Built-in (in-memory fallback) | @objectstack/service-i18n |
| 12 | file-storage | optional |
— | ❌ Plugin Required | TBD plugin |
| 13 | search | optional |
— | ❌ Plugin Required | TBD plugin |
| 14 | cache | core |
— | ✅ Built-in (in-memory fallback) | @objectstack/service-cache |
| 15 | queue | core |
— | ✅ Built-in (in-memory fallback) | @objectstack/service-queue |
| 16 | job | core |
— | ✅ Built-in (in-memory fallback) | @objectstack/service-job |
| 17 | graphql | optional |
— | ❌ Plugin Required | TBD plugin |
Service Name: metadata · Criticality: core
Implementation: Kernel — SchemaRegistry (in-memory)
Route Mounts: /api/v1/meta, /api/v1, /api/v1/packages
| Method | Signature | Status |
|---|---|---|
getDiscovery |
GetDiscoveryRequest → GetDiscoveryResponse |
✅ |
getMetaTypes |
GetMetaTypesRequest → GetMetaTypesResponse |
✅ |
getMetaItems |
GetMetaItemsRequest → GetMetaItemsResponse |
✅ |
getMetaItem |
GetMetaItemRequest → GetMetaItemResponse |
✅ |
saveMetaItem |
SaveMetaItemRequest → SaveMetaItemResponse |
✅ |
getMetaItemCached |
GetMetaItemCachedRequest → GetMetaItemCachedResponse |
✅ |
getUiView |
GetUiViewRequest → GetUiViewResponse |
✅ |
The metadata service is a framework — it works with an in-memory SchemaRegistry loaded from YAML/TS config files at startup.
| Gap | Description | Priority |
|---|---|---|
| DB Persistence | Metadata is only in memory; no save-to-database path exists | P0 |
| Multi-instance Sync | No mechanism to share metadata changes across instances | P1 |
| Migration / Versioning | No schema diff or migration tooling | P1 |
| Hot Reload | Metadata changes require restart | P2 |
The discovery endpoint returns a services map so clients know what is available:
{
"services": {
"metadata": {
"enabled": true, "status": "degraded",
"route": "/api/v1/meta", "provider": "kernel",
"message": "In-memory registry; DB persistence pending"
},
"data": {
"enabled": true, "status": "available",
"route": "/api/v1/data", "provider": "kernel"
},
"auth": {
"enabled": false, "status": "unavailable",
"message": "Install an auth plugin to enable"
}
}
}Service Name: data · Criticality: required
Implementation: @objectstack/objectql → ObjectQL (IDataEngine)
Route Mount: /api/v1/data
| Method | Signature | Status |
|---|---|---|
findData |
FindDataRequest → FindDataResponse |
✅ |
getData |
GetDataRequest → GetDataResponse |
✅ |
createData |
CreateDataRequest → CreateDataResponse |
✅ |
updateData |
UpdateDataRequest → UpdateDataResponse |
✅ |
deleteData |
DeleteDataRequest → DeleteDataResponse |
✅ |
batchData |
BatchDataRequest → BatchDataResponse |
✅ |
createManyData |
CreateManyDataRequest → CreateManyDataResponse |
✅ |
updateManyData |
UpdateManyDataRequest → UpdateManyDataResponse |
✅ |
deleteManyData |
DeleteManyDataRequest → DeleteManyDataResponse |
✅ |
| Driver | Package | CRUD | Aggregation | Ready |
|---|---|---|---|---|
| InMemory | @objectstack/driver-memory |
✅ | ✅ | Dev/Test |
| PostgreSQL | TBD | — | — | — |
| MySQL | TBD | — | — | — |
| MongoDB | TBD | — | — | — |
| SQLite | TBD | — | — | — |
Service Name: analytics · Criticality: optional
Implementation: @objectstack/objectql (driver-level capability)
Route Mount: /api/v1/analytics
| Method | Signature | Status |
|---|---|---|
analyticsQuery |
AnalyticsQueryRequest → AnalyticsResultResponse |
✅ |
getAnalyticsMeta |
GetAnalyticsMetaRequest → AnalyticsMetadataResponse |
✅ |
- Cube semantic queries: measures / dimensions / filters → ObjectQL aggregation
- Auto-generated metadata from SchemaRegistry (numeric → sum/avg, date → time dimensions)
Service Name: auth · Criticality: core
Route Mount: /api/v1/auth — only exposed when a plugin registers the auth service
The auth service covers both authentication (identity) and authorization (permissions). There is no separate permission service in CoreServiceName.
| Module | Area | Description | Priority |
|---|---|---|---|
| Identity Provider | Authentication | JWT issuance/verification, session management | P0 |
| User CRUD | Authentication | Create / read / update / delete users | P0 |
| Role Management | Authentication | Role definitions, role-user associations | P0 |
| Permission Engine | Authorization | Object-level and field-level permissions | P0 |
| OAuth2 / OIDC | Authentication | Third-party login (Google, GitHub, etc.) | P1 |
| Sharing Rules | Authorization | Record-level sharing and visibility | P1 |
| Row-Level Security | Authorization | Automatic query filtering by user context | P1 |
| Multi-tenancy | Authentication | Space / Tenant isolation | P1 |
| Territory Management | Authorization | Data territory assignment and access | P2 |
| SCIM | Authentication | Enterprise user provisioning protocol | P2 |
listViews, getView, createView, updateView, deleteView
View CRUD, layout engine, action registry, permission filtering.
getWorkflowConfig, getWorkflowState, workflowTransition
State machine transitions. Approve/reject are no longer workflow methods — per ADR-0019 they moved to the request-id-based approvals API under /api/v1/approvals.
triggerAutomation
Trigger engine, event triggers from ObjectQL hooks, flow executor, scheduled triggers.
realtimeConnect, realtimeDisconnect, realtimeSubscribe, realtimeUnsubscribe, setPresence, getPresence
registerDevice, unregisterDevice, getNotificationPreferences, updateNotificationPreferences, listNotifications, markNotificationsRead, markAllNotificationsRead
aiNlq, aiSuggest, aiInsights
getLocales, getTranslations, getFieldLabels
Service Name: i18n · Criticality: core
Implementations: @objectstack/service-i18n (production — file-based) · Built-in in-memory fallback · Dev Plugin (in-memory stub)
Route Mount: /api/v1/i18n
Contract: II18nService in @objectstack/spec/contracts
The i18n service is a core built-in service with automatic in-memory fallback. If no plugin registers the service, the kernel automatically injects an in-memory implementation during startup:
| Environment | Provider | Registration |
|---|---|---|
| Production | I18nServicePlugin |
File-based FileI18nAdapter loads JSON locale files from disk |
| Built-in Fallback | Kernel | In-memory Map-backed stub, auto-injected when no plugin provides i18n |
| Development | DevPlugin |
In-memory Map-backed stub, supports loadTranslations() |
// Production (overrides built-in fallback)
kernel.use(new I18nServicePlugin({ defaultLocale: 'en', localesDir: './i18n' }));
// Development (automatic — DevPlugin registers i18n stub for all 17 core services)
kernel.use(new DevPlugin());
// No plugin required — kernel auto-injects in-memory fallback if none registered
const kernel = new ObjectKernel();
await kernel.bootstrap(); // i18n service available via built-in fallbackThe Discovery API (/api/v1 or /.well-known/objectstack) and the i18n route handler (/api/v1/i18n/*) both use the same async resolution chain to detect i18n availability:
getServiceAsync() → getService() → context.getService() → services Map
This ensures that discovery.services.i18n.status always matches the actual runtime behavior — a service registered via any mechanism (sync Map, async factory, or context) will be reported correctly in both places.
The locale field in the discovery response is populated from the actual i18n service:
locale.default— fromi18nService.getDefaultLocale()(falls back to'en')locale.supported— fromi18nService.getLocales()(falls back to[default])
When an app bundle includes an i18n config and translations array, AppPlugin automatically loads the translation data into the i18n service during the start phase:
export default defineStack({
manifest: { id: 'com.example.crm', namespace: 'crm' },
i18n: { defaultLocale: 'en', supportedLocales: ['en', 'zh-CN'] },
translations: [CrmTranslations], // TranslationBundle[]
});AppPlugin will:
- Set the default locale via
i18nService.setDefaultLocale() - Call
i18nService.loadTranslations(locale, data)for each locale in every bundle - Skip gracefully if no i18n service is registered (no errors, just a debug log)
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/i18n/locales |
List available locales |
GET |
/api/v1/i18n/translations/:locale |
Get all translations for a locale |
GET |
/api/v1/i18n/labels/:object/:locale |
Get translated field labels for an object |
cache, queue, and job are core services: like i18n, the kernel auto-injects an in-memory fallback when no plugin registers them (see CORE_FALLBACK_FACTORIES in packages/core/src/fallbacks/). The optional services (file-storage, search, graphql) stay disabled until a plugin provides them.
| Service | Description |
|---|---|
| file-storage | Unified upload/download/delete. Drivers: local FS, S3, MinIO. |
| search | Full-text search. Drivers: in-memory, Elasticsearch, Meilisearch. |
| cache | General-purpose cache. In-memory fallback; Redis via @objectstack/service-cache. |
| queue | Message queue. In-memory fallback; BullMQ / Redis via @objectstack/service-queue. |
| job | Scheduled task execution. In-memory fallback; cron-based, concurrency control. |
| graphql | Auto-generate GraphQL schema from Object metadata. |
| Plugin | Description |
|---|---|
| plugin-auth | Identity engine — JWT, sessions, user CRUD, roles |
| plugin-ui | View CRUD — core low-code capability |
| plugin-cache | General-purpose cache — performance infrastructure |
| Metadata DB | Solve metadata persistence (SchemaRegistry → database) |
| Plugin | Description |
|---|---|
| service-automation | Flow orchestration, state-machine adjacent execution, approval-node pauses |
| plugin-automation | Triggers + flow orchestration |
| plugin-i18n | Internationalization |
| plugin-storage | File storage (local + S3) |
| DB drivers | PostgreSQL, MySQL, MongoDB, SQLite |
| Plugin | Description |
|---|---|
| plugin-realtime | WebSocket / SSE real-time push |
| plugin-notification | Notification engine |
| plugin-ai | NLQ, chat, suggestions, insights |
| plugin-graphql | GraphQL API endpoint |
| plugin-search | Full-text search |
| plugin-job | Scheduled tasks |
| plugin-queue | Message queue |
import type { Plugin } from '@objectstack/core';
let authService: AuthServiceImpl;
export const authPlugin: Plugin = {
name: 'plugin-auth',
async init(ctx) {
// Register the 'auth' CoreServiceName value
const engine = ctx.getService<IDataEngine>('data');
authService = new AuthServiceImpl(engine);
ctx.registerService('auth', authService);
},
async start(ctx) {
const auth = ctx.getService<IAuthService>('auth');
await auth.onReady();
},
// destroy() takes no arguments — capture what you need in module scope
async destroy() {
await authService.shutdown();
},
};When a plugin registers a service, the discovery endpoint automatically updates:
services.auth.enabled→true,status→"available",route→"/api/v1/auth"routes.auth→"/api/v1/auth"appears in routesfeaturescapability flags update accordingly