---
title: "Metadata Service"
description: The unified engine for loading, saving, and managing configuration metadata across the platform
---

import { Database, FileCode, Layers, RefreshCw, Save } from 'lucide-react';

The **Metadata Service** is the backbone of ObjectOS configuration management. It decouples the *definition* of metadata (Objects, Views, Flows) from its runtime delivery path: local development reads files or `dist/objectstack.json`, while production ObjectOS reads an immutable artifact from the control plane. Project databases store business rows only.

## Architecture

The Metadata Service uses a **Provider/Loader** architecture. The core `MetadataManager` acts as a registry and orchestrator, delegating actual I/O operations to registered **Loaders**.

<Cards>
  <Card
    icon={<Layers />}
    title="Registry Pattern"
    description="The Manager accepts any number of Loaders. Runtime ObjectOS uses file/artifact loaders; control-plane services can opt into database loaders explicitly."
  />
  <Card
    icon={<FileCode />}
    title="Format Agnostic"
    description="Automatically handles JSON, YAML, and TypeScript formats. Developers write TS, Machines write JSON."
  />
  <Card
    icon={<RefreshCw />}
    title="Hot Reload"
    description="Built-in file watching. Edit a file in VS Code, and the runtime updates immediately."
  />
  <Card
    icon={<Save />}
    title="Atomic Persistence"
    description="Safe save API with backup generation and atomic writes to prevent data corruption."
  />
</Cards>

## Core Concepts

### 1. The Manager

The `MetadataManager` is the single entry point for all metadata operations. It handles caching, serialization, and correct routing of requests to loaders.

```typescript
import { MetadataManager } from '@objectstack/metadata';

const manager = new MetadataManager({
  rootDir: './src',
  watch: true // Enable hot reload
});

// Load an Object Definition
const accountObj = await manager.load('object', 'account');
```

### 2. Loaders (Data Sources)

Loaders adapt different storage backends to a common interface.

| Loader | Type | Description |
| :--- | :--- | :--- |
| **FilesystemLoader** | `filesystem` | Reads/Writes `.json`, `.yaml`, `.ts` files from disk. Primary for development. |
| **MemoryLoader** | `memory` | Hydrates compiled artifact items into the runtime metadata registry. |
| **DatabaseLoader** | `database` | Stores metadata revisions in control-plane services that explicitly configure it. Not auto-enabled by ObjectOS. |
| **RemoteLoader** | `http` | Fetches metadata from a remote API or git repository. |

### 3. Granular Capabilities

Each Loader declares its capabilities via a **Contract**:

```typescript
export interface MetadataLoaderContract {
  name: string;
  protocol: 'file' | 'http' | 'database';
  capabilities: {
    read: boolean;  // Can read items?
    write: boolean; // Can save modifications?
    watch: boolean; // Can push real-time updates?
    list: boolean;  // Can enumerate items?
  };
}
```

## Internal Persistence API

The Metadata Service supports full CRUD operations for authoring tools and control-plane services. Runtime ObjectOS treats metadata as read-only after artifact/file hydration; `MetadataPlugin` does not automatically bridge ObjectQL to `DatabaseLoader`.

### Saving Metadata (Node.js)

```typescript
// 1. Simple Save (Auto-detects loader)
await manager.save('object', 'customer_profile', data);

// 2. Targeted Save (Specific Source)
await manager.save('object', 'customer_profile', data, {
  loader: 'filesystem',
  format: 'yaml', // Convert to YAML on save
  atomic: true    // Use write-rename strategy for safety
});

// 3. Create Backup
await manager.save('object', 'workflow_config', data, {
  backup: true // Creates 'workflow_config.json.bak' before writing
});
```

### Loading Strategy

When `load()` is called, the Manager iterates through registered loaders. In ObjectOS runtime boot this means "artifact/file first"; database-backed metadata is reserved for explicitly configured control-plane services.

```mermaid
graph LR
    A[Request: load('object', 'user')] --> B{Metadata Manager}
    B --> C[Check Artifact / Memory Loader]
    C -- Not Found --> D[Check Filesystem Loader]
    D -- Found --> E[Return & Cache]
    C -- Found --> E
```

## Watching & Hot Reload

The Service emits events when metadata changes, allowing the runtime to reconfigure itself without restarting.

```typescript
manager.watch('object', (event) => {
  console.log(`Object ${event.name} changed!`);
  console.log(`New Schema:`, event.data);
  
  // Trigger system update
  SchemaEngine.refresh(event.name);
});
```

## Remote API Access

<Callout type="info">
  ObjectOS metadata is artifact-backed at runtime. Read/write metadata APIs belong to authoring tools and the control plane.
</Callout>

The system provides standard REST and TypeScript APIs for interacting with metadata, including **full read/write support**.

### REST API

- `GET /api/metadata`: List all metadata types.
- `GET /api/metadata/:type`: List items of a type.
- `GET /api/metadata/:type/:name`: Get simplified definition of an item.
- `PUT /api/metadata/:type/:name`: Save or update a metadata item.

### Client SDK

```typescript
// Save metadata from the browser
const result = await client.meta.saveItem('object', 'customer', {
  label: 'Customer',
  fields: {
    name: { type: 'text', required: true }
  }
});
```

### Server-Side Protocol

```typescript
// Access from Protocol Service (Server-side)
const protocol = kernel.getService('protocol');
await protocol.saveMetaItem({ 
  type: 'object', 
  name: 'lead', 
  item: { ... } 
});
```


## Runtime Controls (v3.1+)

Four orthogonal levers shape how `MetadataManager` and `MetadataPlugin` behave at boot and at request time. All four default to "dev-friendly" so existing code keeps working.

### 1. Bootstrap Modes

`MetadataPluginConfigSchema.bootstrap` decides what the plugin does on `start()`:

| Mode | When to use | Behavior |
| :--- | :--- | :--- |
| `eager` (default) | Local dev, traditional servers | Scans filesystem (or hydrates from `artifactSource`) at boot. |
| `lazy` | Cold-start sensitive runtimes, on-demand workloads | Skips the FS prime; reads flow through registered loaders (incl. DatabaseLoader cache). Honors `artifactSource` when set. |
| `artifact-only` | Edge / serverless / read-only production | Refuses to touch the filesystem. Requires `artifactSource.mode = 'local-file'`. |

```typescript
new MetadataPlugin({
  config: { bootstrap: 'artifact-only' },
  artifactSource: { mode: 'local-file', path: './dist/objectstack.json' },
});
```

### 2. Persistence Write Gates

`MetadataManagerConfigSchema.persistence` is a two-axis runtime freeze. Both flags default to `true`.

| Flag | Effect when `false` |
| :--- | :--- |
| `persistence.writable` | `register()` becomes a no-op (or throws when `validation.throwOnError`). |
| `persistence.overlayWritable` | `saveOverlay()` is rejected. Disables Studio overlays in sealed deployments. |

```typescript
new MetadataManager({
  persistence: { writable: false, overlayWritable: false },
});
```

### 3. DatabaseLoader Read-Through Cache

`DatabaseLoader` wraps `load` / `loadMany` / `list` / `stat` results in a generic LRU cache (lazy TTL, promote-on-get, write invalidation). Reads always observe writes performed through the same loader instance; out-of-band SQL writes are honored within `ttl` milliseconds.

```typescript
new MetadataManager({
  datasource: 'default',
  cache: {
    enabled: true,
    databaseLoader: { enabled: true, maxSize: 500, ttl: 60_000 },
  },
});
```

The cache exposes `LRUCache.stats()` (`size` / `hits` / `misses` / `hitRate`) for metrics.

### 4. Single-Source Schema Discipline

The canonical `MetadataManagerConfigSchema` and `MetadataFallbackStrategySchema` live in `kernel/metadata-loader.zod.ts`. The persistence-side module `system/metadata-persistence.zod.ts` re-exports them, so consumers observe a single TypeScript type regardless of import path. Drift between kernel-side runtime config and persistence-side typing is no longer possible.

## CLI & Tooling Integration

The Metadata Service is designed to power the CLI.

- **`objectstack compile`**: Validates TypeScript metadata and emits `dist/objectstack.json`.
- **`objectstack publish`**: Uploads compiled JSON to the control plane, which creates a metadata revision and artifact.
- **`objectstack dev`**: Boots ObjectOS from local files or the compiled artifact without a control-plane connection.
