North Star
The product shape of ObjectStack - AI-native business backend, code-first metadata authoring, control-plane artifacts, Studio observability, and stateless ObjectOS runtimes.
North Star
This document is the product-shape blueprint for ObjectStack. The sibling documents (
architecture.mdx,metadata-driven.mdx) describe the microkernel internals. This one describes what users actually do and what the platform looks like from the outside. When an architecture decision is unclear, read this first.
1. Mission
ObjectStack is an AI-native business backend. Users author data shape, business logic, permissions, UI metadata, and agent-facing tools as declarative metadata in a local TypeScript workspace. The platform compiles that metadata into JSON artifacts, publishes each artifact as an immutable Project Version in the control plane, and serves running applications from the Project's current Version - no glue code, no hand-written CRUD, no separate frontend/backend codebases, and no manually duplicated agent tool definitions.
Phase 1 is deliberately code-first:
- The local TypeScript workspace is the only metadata authoring surface.
- The CLI compiles metadata and publishes a new Project Version to the control plane.
- Studio is a hosted control-plane dashboard for project configuration, version history, artifact inspection, logs, and health. It does not modify user metadata in Phase 1.
- ObjectOS runtimes are stateless consumers of the Project's current Version artifact.
Six non-negotiable tenets:
- Agent-Ready Business Metadata - objects, fields, functions, views, flows, permissions, and tools are all explicit metadata artifacts
- Compact by Construction - because the whole business system is declarative metadata, a typical enterprise app needs roughly two orders of magnitude less code (~100× fewer lines to write and maintain) than a hand-written equivalent. Small enough for an AI agent to load end-to-end, reason about every dependency, and safely refactor across data, API, UI, and permissions in a single change - this is the real moat, not "low-code UI"
- Versioned Artifacts, Stateless Runtime - every publish creates an immutable Project Version; ObjectOS runtimes pull the current Version artifact and serve it
- TS-Authored, JSON-Stored - TypeScript is the authoring form (type safety, IDE intelligence, AI-assisted editing); JSON is the wire and storage form (Project Version artifact payload, Artifact API response body, local artifact cache). Zod schemas are the only bridge between the two.
- Local-First (development) - every project can run against local drivers (memory, SQLite, embedded Turso replica); cloud is an enhancement, not a dependency. This refers to the developer/runtime mode, not to data sovereignty on end-user devices.
- Open Core - this repository is Apache-2.0 licensed as the open-source framework foundation; enterprise editions, official cloud services, and marketplace commercial terms live outside this repository
2. Product Shape
ObjectStack's first production shape is code-first AI-native metadata deployment: developers author a TypeScript metadata tree locally, compile it to JSON, publish it as an immutable Project Version in the control plane, inspect the version and artifact in Studio, and run the Project's current Version through ObjectOS. The same artifact is the source for APIs, UI metadata, permission checks, workflows, and agent tool surfaces.
Branching, visual metadata editing, and pull/merge workflows are future phases. The Phase 1 goal is smaller and more important: make the artifact boundary real and make every publish a durable, rollback-capable Version.
Local Workspace + CLI
TS-authored project tree. `compile` turns it into JSON; `publish` creates an immutable Project Version.
Studio Dashboard
Create orgs and projects. Inspect versions, artifacts, publish history, logs, and health. Phase 1 is read-only for user metadata.
Artifact API
Control-plane endpoint that returns a project's current Version artifact. HTTP today, S3-backed tomorrow.
ObjectOS Runtime
Stateless artifact consumer. Pulls once, caches, serves API + UI, and survives control-plane outages.
| Developer / Cloud Concept | ObjectStack Phase 1 |
|---|---|
| Organization | Organization |
| Hosted project | Project |
| Working tree | Local TS file tree (objectstack.config.ts + src/objects/*.ts + ... ) |
| Build | objectstack compile - validates TS, writes dist/objectstack.json |
| Deploy | objectstack publish - creates a new immutable Project Version for one project |
| Dashboard | Studio - project management, version history, artifact inspection, logs, health |
| Runtime boot | ObjectOS pulls current Version artifact -> hydrates kernel -> serves app |
| Artifact storage | Project Version artifact JSON or payload reference, validated by Zod |
| Metadata projection | Optional derived indexes / summaries; full metadata browsing is not required for Phase 1 |
| Business rows | Per-project business DB, never the control-plane DB |
| Project URL | Project hostname |
The deliberate simplification is that Phase 1 has one writer for user metadata: the CLI publishing from the local TypeScript workspace. Studio can show version history, artifact payloads, generated tool surfaces, permission boundaries, and runtime state, but it cannot edit metadata. This keeps the core loop tight:
TS authoring -> compile -> publish Project Version -> current artifact -> ObjectOS runtime3. Primary Surfaces
Four user-facing surfaces. Everything else is infrastructure.
Local Workspace + CLI
Authoring surface. A scaffolded TypeScript file tree driven by the objectstack
CLI. Exists because AI coding assistants, IDE intellisense, and Zod type
inference work best when metadata is authored in TS. JSON is a storage and wire
format, not the human authoring format.
Shape:
objectstack.config.tscallingdefineStack({...})- Object / view / flow / agent files under
src/*/usingObjectSchema.create(),Field.*,defineView(),defineAgent()etc. - Scaffolded via
npx create-objectstack. See packages/create-objectstack/src/index.ts.
Core commands:
objectstack dev- boots a local ObjectOS against the local TS tree, fully offline (no control-plane call)objectstack compile- validates TS againstObjectStackDefinitionSchemaand emitsdist/objectstack.json. See packages/cli/src/commands/compile.ts.objectstack publish- POSTs the compiled JSON to the control plane for the current project, creating a new immutable Project Version. Today routes to/api/v1/packages(legacy) - see packages/cli/src/commands/publish.ts; it must evolve to a project publish endpoint that returnsversionId,versionNumber,commitId, andchecksum(Drift in §7).
Relationship to Studio: the CLI is the Phase 1 metadata writer. Studio is a reader for project configuration, Project Versions, artifacts, publish history, runtime health, logs, and usage signals. Bidirectional CLI/Studio editing is intentionally deferred.
Version model: every successful publish creates an immutable Project Version
with versionId, versionNumber, commitId, and a content checksum. A Project
has a current Version pointer that controls what ObjectOS serves. Rollback is a
pointer change, not artifact mutation. These identifiers are not yet a
collaborative merge or bidirectional write protocol.
Studio Dashboard
Cloud-hosted web UI. In Phase 1 Studio is the control-plane console, not a metadata authoring surface.
Responsibilities:
- Org / member / billing management
- Project lifecycle (create, configure, delete)
- Project version history (current version, latest version, rollback/promote state)
- Artifact inspector (schema version, version id, checksum, commit id, publish time, payload preview)
- API console, logs, usage metrics, runtime health
- Publish history and diagnostics
Explicit Phase 1 boundary: Studio does not edit user metadata. Visual metadata editors, metadata browsing as a primary product surface, Studio writes, and collaborative conflict handling belong to a future phase after the versioned-artifact runtime path is stable.
Status: ~70% built for org/project/member UI. Version history, artifact viewer, and runtime observability need to be added. See apps/studio/src/routes/.
Control Plane Artifact API
The protocol boundary between the control plane and ObjectOS runtimes. A single well-known HTTP endpoint returns everything ObjectOS needs to boot a project.
GET /api/v1/cloud/projects/:projectId/artifact
-> { schemaVersion, projectId, versionId, commitId, checksum, metadata, functions, manifest }Today (M3 built): GET /api/v1/cloud/projects/:projectId/artifact is live — assembles the artifact from sys_project.metadata.artifact_path / artifact_paths[], merges metadata bundles, and returns the ProjectArtifact envelope with commitId and checksum. GET /api/v1/cloud/resolve-hostname resolves a hostname to a project id. See packages/services/service-cloud/src/cloud-artifact-api-plugin.ts.
Phase 1 next step: wire proper Project Versions so the endpoint returns versionId / versionNumber and ObjectOS can use them for ETag cache invalidation.
Future: the same endpoint can return a redirect or signed URL pointing at an
S3 object. The interface is stable; the backend swaps.
Phase 1 target: the control plane returns the Project's current Version
artifact. A pinned endpoint can later return a specific immutable version:
GET /api/v1/cloud/projects/:projectId/versions/:versionId/artifact.
Future: the same endpoint can return a redirect or signed URL pointing at an
S3 object. The interface is stable; the backend swaps.
Responsibilities:
- Serve the current Version's metadata + inlined function code as a single consumable blob
- Expose
versionId,commitId, content hash, and ETag so runtimes can detect drift and decide when to refetch - Leave room in the response shape for future
{ url, expiresAt, checksum }indirection - Be cacheable at the CDN edge (future; read-heavy by design)
Status: M3 + D2 built. Core endpoint live; Project Versions (immutable versionId / versionNumber) are the next step (M5).
ObjectOS Runtime
The per-project runtime kernel. Stateless with respect to metadata - everything it knows comes from an artifact pulled over HTTP from the control plane. Connects to an independent business database for the project's actual data rows.
Responsibilities:
- Resolve incoming request -> project kernel via hostname
- On first request for a project: pull artifact from Artifact API -> deserialize into kernel
- Cache artifact locally; continue serving even if the control plane is unreachable
- Auto-generate REST API from the artifact's object schemas
- Auto-generate UI schemas (Amis/React-renderable)
- Execute object functions in sandboxed runtime
- Query/mutate the project's business DB (not the control-plane DB) through ObjectQL
- Enforce permissions, emit audit events back to the control plane (best-effort)
Status: Multi-project kernel + hostname routing built. local-file artifact
boot (M1.x) implemented — ObjectOS can boot from dist/objectstack.json without
any DB read. D1 is resolved: MetadataPlugin no longer registers
sys_metadata / sys_metadata_history into the ObjectOS manifest and no longer
auto-bridges ObjectQL to DatabaseLoader after startup. See
packages/runtime/src/project-kernel-factory.ts
and packages/metadata/src/plugin.ts.
4. Core Entity Hierarchy
Everything in the control plane; business data lives alongside but isolated per project.
Organization <- control plane
├── Members (with roles)
├── Billing
└── Project <- control plane; binds primary hostname
├── Current Version Pointer <- current_version_id
├── Project Versions <- immutable publish history
│ ├── version_id / version_number
│ ├── commit_id / checksum
│ └── Artifact JSON or payloadRef
├── Artifact API <- control-plane response shape
│ └── Current Version compiled JSON document
└── Business DB <- per-project, independent
└── Rows of user objects (customer, order, ...)
!! Does NOT store metadata.| Entity | Location / Table | Status |
|---|---|---|
| Organization | Control plane sys_organization | Built |
| Project | Control plane sys_project (hostname binding) | Built |
| Project Version | Control-plane immutable artifact release (version_id, version_number, commit_id, checksum, artifact JSON or payload reference) | To build |
| Current Version Pointer | sys_project.current_version_id (target shape) controls the version ObjectOS serves | To build |
| Artifact | GET /api/v1/cloud/projects/:projectId/artifact response for the current Version | To build |
| Business DB | Per-project dedicated DB (Turso / SQL / memory) | Built (driver provisioning), but currently shares space with metadata |
| Branch | Future control-plane entity that points to a version and owns runnable branch config | Deferred |
See packages/services/service-tenant/src/objects/sys-project.object.ts for the current Project schema.
Authoring vs Storage
Metadata has two physical forms in ObjectStack, and the boundary between them is strict:
- Authoring form - TypeScript. Developers edit
objectstack.config.tsandsrc/*files locally. The thing humans and AI agents interact with is typed TypeScript backed by Zod schemas. - Storage / wire form - JSON. Everything that hits a disk or a network - the Project Version artifact payload, the Artifact API response body, the local artifact cache, and any future object-storage payload reference - is JSON. It is small, portable, and has no dependency on the TypeScript compiler.
The bridge between the two is Zod, nothing else:
- CLI
compilevalidates the local TS tree againstObjectStackDefinitionSchema.safeParseand emits JSON. - The control plane validates incoming JSON with the same Zod schemas before creating a Project Version.
- ObjectOS validates the artifact with Zod on load.
- JSON Schema (published via
z.toJSONSchema()in packages/spec/scripts/build-schemas.ts) powers IDE validation and CLI error messages.
If Studio later needs rich metadata browsing, diffing, or visual editing, those
features read directly from the artifact in memory (the same source the
runtime uses). The control plane does not materialise artifact contents
into sys_metadata — that table is reserved exclusively for per-organisation
overlay deltas (ADR-0005, ADR-0008 §0). Artifact change history lives in Git
and in the deployment log; see the
FAQ in Metadata Lifecycle
for the full rationale.
Any serializer that bypasses Zod is an anti-pattern - see §8. The Zod schema is the protocol.
5. Operational Architecture - Three Vertices, One Protocol
This is how the platform runs, not how the code is organized. The control plane sits at the center of a triangle: Local Workspace pushes compiled JSON in; Studio reads and visualizes control-plane state; ObjectOS runtimes pull the compiled artifact out over HTTP. One HTTP contract (the Artifact API) carries everything that leaves the control plane for runtime boot.
┌─ Local Workspace ──────────┐ ┌─ Studio (UI) ─────────┐
│ objectstack.config.ts (TS) │ │ Project dashboard │
│ src/objects/*.ts (TS) │ │ Version history │
│ src/views/*.ts (TS) │ │ Artifact inspector │
│ │ │ Logs / health │
│ objectstack compile │ │ │
│ -> dist/objectstack.json │ │ │
└────────────┬───────────────┘ └───────────▲───────────┘
│ publish │ read-only APIs
▼ │
┌─────────────────────────────────────────────────────────────┐
│ Control Plane (singleton, cloud-hosted) │
│ ────────────────────────────────────── │
│ Studio UI (React) + Hono backend │
│ │
│ Platform metadata: │
│ sys_organization · sys_project · sys_user · sys_app │
│ Auth · Billing · Audit │
│ │
│ Project Versions (all projects): │
│ Immutable publish artifacts │
│ - artifact JSON or payloadRef │
│ - version_id / version_number / commit_id / checksum │
│ - current_version_id pointer on sys_project │
│ - extracted summaries / indexes for Studio views │
│ │
│ Artifact API: │
│ GET /api/v1/cloud/projects/:projectId/artifact │
│ today : returns current Version artifact │
│ future: pinned version endpoint + S3 redirect / signed URL│
│ │
│ Hostname routing: host -> project │
└──────────────────────────┬──────────────────────────────────┘
│ Artifact over HTTP <- the protocol
▼
┌─────────────────────────────────────────────────────────────┐
│ ObjectOS Runtimes (one ObjectKernel per active project) │
│ ────────────────────────────────────── │
│ Two boot paths, one kernel: │
│ (a) from-artifact-api - production, cloud ObjectOS │
│ pull JSON over HTTP -> hydrate kernel -> cache locally│
│ (b) from-local-file - `objectstack dev`, fully offline │
│ read dist/objectstack.json (or in-memory TS) -> run │
│ │
│ Serve: auto-generated REST + UI schema from metadata │
│ Data : business driver -> per-project business DB │
│ (no metadata queries; business rows only) │
└─────────────────────────────────────────────────────────────┘The immutability of the artifact is a Phase 1 product boundary. Each publish
creates a new Project Version; the Project's current Version pointer decides
what production ObjectOS serves. versionId, commitId, and checksum
identify exactly which bytes a runtime loaded. Rollback and promotion mutate
pointers, never Version payloads.
Two vertices, two apps, one framework. The Control Plane vertex and the
ObjectOS Runtime vertex are both realized as ObjectStack-framework apps booted
from their own objectstack.config.ts. They share the same ObjectKernel,
spec, and adapter stack - they differ only in the plugin manifest they
register. Today apps/objectos/objectstack.config.ts
is a hybrid that loads both manifests in one process; the target shape is two apps:
| Plugin | apps/cloud (Control Plane) | apps/objectos (ObjectOS) |
|---|---|---|
createControlPlanePlugins(...) (ObjectQL on control DB + driver + system-project + sys_* metadata) | Yes | - |
MultiProjectPlugin (env-registry, kernel-manager, template-seeder) | Yes | - |
AuthPlugin | Yes | Yes |
createTenantPlugin(...) | Yes | Yes |
SecurityPlugin | Yes | Yes |
AuditPlugin | Yes | Yes |
SetupPlugin (Studio bootstrap) | Yes (optional) | - |
ObjectQLPlugin (project-scoped) | - | Yes |
MetadataPlugin (artifact-loader mode) | - | Yes |
User-app AppPlugin (compiled app) | - | Yes |
Identity plugins (Auth/Tenant/Security/Audit) are installed on both sides: a request to either vertex must resolve the same user/org/role. Splitting identity would require federated auth, which is out of scope.
Key code anchors:
- Control-plane app (target): apps/cloud/objectstack.config.ts (D8 complete)
- ObjectOS app (target): apps/objectos/objectstack.config.ts (currently hybrid; will be reduced to ObjectOS manifest)
- Hostname -> project resolver: packages/runtime/src/environment-registry.ts
- Per-project kernel factory: packages/runtime/src/project-kernel-factory.ts
- HTTP dispatch: packages/runtime/src/http-dispatcher.ts
- Org-scoped proxy: packages/runtime/src/control-plane-proxy-driver.ts
- Metadata loading (artifact/local-file backed; no automatic ObjectQL ->
DatabaseLoaderbridge in ObjectOS): packages/metadata/src/plugin.ts - App catalog sync: packages/services/service-tenant/src/services/app-catalog.service.ts
6. Key Lifecycles
6.0 Local Development and Push
npx create-objectstack my-app # scaffold TS file tree (objectstack.config.ts + src/*)
cd my-app
objectstack dev # boot ObjectOS from local TS - no control-plane call
... iterate: edit src/objects/*.ts, Fast Refresh ...
objectstack compile # validate TS -> emit dist/objectstack.json
objectstack login # obtain auth token for the target control plane
objectstack publish # POST JSON, create Project Version
# returns versionId + commitId + checksumTwo things to notice:
objectstack devis fully offline. The local ObjectOS reads the TS tree (or the compileddist/objectstack.json) directly. It does not hit the Artifact API, and it does not need login/network.publishcreates an immutable Project Version. In Phase 1 the local TS workspace is the only metadata writer, soversionId,commitId, and checksum are primarily release-history, audit, rollback, and cache-validation identifiers.
6.1 Onboarding
signup -> create org -> create project
-> control plane allocates primary hostname
-> control plane provisions per-project business DB (Turso / SQL / memory)
-> Studio lands on the project overview
-> ObjectOS does NOT boot yet - it boots lazily on first request6.2 Publish and Inspect
developer runs objectstack publish
-> control plane validates JSON with Zod
-> creates immutable Project Version with artifact JSON or payloadRef
-> records versionId, versionNumber, commitId, and checksum
-> advances project current_version_id (auto-promote in Phase 1)
-> Studio shows current version, artifact envelope, publish history, and validation state
-> ObjectOS pulls the current Version artifact on next boot/refetchStudio is intentionally a viewer and release console here. If a user sees bad metadata in an artifact, the fix is to edit the local TS files and publish a new Version, or roll back the Project's current Version pointer to a known-good Version.
6.3 Runtime Inputs
ObjectOS 的运行时输入分两类,缺一不可。把两类混在一起就破坏了 §2「Versioned Artifacts, Stateless Runtime」与 §8「ObjectOS 只通过 Artifact API 接触控制面」的边界。
A. Artifact(不可变、可缓存、随 metadata 变更而变更)
ProjectArtifactSchema envelope,定义在 packages/spec/src/cloud/project-artifact.zod.ts:
{ schemaVersion, projectId, versionId?, commitId, checksum, metadata, functions, manifest, builtAt, builtWith, payloadRef? }metadata 字段承载声明性元数据(objects、views、flows、agents 等),functions 内联可执行源码,manifest 声明插件 / driver / engine 要求。Envelope 在外面包一层 versionId / commitId / checksum,让 ObjectOS 能做完整性校验、ETag 校验和缓存失效。payloadRef 为未来 S3 / signed URL 旁路保留,不需要 envelope 破坏性升级。
B. Deployment Config(可变、不进 artifact、与元数据正交)
ObjectOS 启动时必须从外部注入:
- 业务 DB 坐标(URL / driver / 凭据)
- 项目身份(
projectId/organizationId/hostname) - 控制面 URL(用于拉 artifact、上报 audit)
- 进程级密钥(
AUTH_SECRET、KMS 密钥)
云端模式下,B 来自控制面 sys_project + sys_project_credential + 进程 env。本地单 project 模式(一个 ObjectOS 进程对应一个 project,不是多租户网关)下退化为扁平 env:
| Env | 用途 | 备注 |
|---|---|---|
OS_PROJECT_ID | 项目身份 | 本地可固定为 proj_local |
OS_DATABASE_URL | 业务 DB URL | 例:file:./.data/app.db |
OS_DATABASE_DRIVER | 业务 DB driver | turso / sqlite / memory |
OS_ARTIFACT_PATH | 本地 artifact 路径 | 默认 ./dist/objectstack.json;填了即跳过 Artifact API |
OS_MODE | 部署模式 | standalone(默认)/ cloud;cloud 时 OS_DATABASE_URL 解释为控制面 DB URL |
OS_MULTI_PROJECT | 多项目开关(已废弃) | 旧别名:=true 等价 OS_MODE=cloud,将于下个 major 移除 |
AUTH_SECRET | 进程级密钥 |
本地模式可以用磁盘 dist/objectstack.json 直接喂内核(用 commitId: 'local-dev' 之类的占位 envelope),不需要控制面在场。
结论:objectstack.json 是元数据(A)的充分输入,但不是运行时的充分输入。任何把业务 DB 连接、密钥、租户身份塞进 objectstack.json 的尝试都是反模式(见 §8)。
6.4 Runtime Request
GET https://proj.app/api/v1/data/customer
↓
ObjectOS gateway receives request -> EnvironmentDriverRegistry resolves
hostname -> projectId
↓
KernelManager looks up ObjectKernel for projectId
↓
If kernel has no artifact loaded:
GET /api/v1/cloud/projects/:projectId/artifact
-> returns current Version artifact
-> hydrate metadata + function code into kernel
-> cache artifact locally (survives control-plane downtime)
↓
REST plugin auto-generates `customer` handler from artifact schema
↓
ObjectQL runs query against the project's BUSINESS DB (not control plane)
↓
Response flows back; audit event emitted to control plane (best-effort)Subsequent requests reuse the cached artifact. Cache invalidation strategy (push / poll / ETag) is an Open Question.
7. Alignment Check
Honest state of the platform as of 2026-05-08, re-classified under the Phase 1 code-first, version-first model. Every future architectural decision should preserve Built, reduce Drift, and advance Missing.
2026-04-13 amendment.
MetaRefno longer carriesprojectorbranch— the metadata layer is keyed purely by(org, type, name). Project survives only as an artifact-packaging concept (theobjectstack.jsonenvelope); branching is left to Git. See ADR-0008 §0 and the Metadata Lifecycle FAQ for the rationale.
Built (Aligned)
- Organization CRUD + member/invitation system - apps/studio/src/hooks/useSession.ts
- Project CRUD + per-project Turso/memory DB provisioning - packages/services/service-tenant/
- Per-project ObjectKernel with LRU cache - packages/runtime/src/project-kernel-factory.ts
- Hostname-based routing:
sys_project.hostname-> kernel resolution - packages/runtime/src/environment-registry.ts - ControlPlaneProxyDriver for org-scoped data isolation - packages/runtime/src/control-plane-proxy-driver.ts
- AppCatalogService (per-project app events -> org-scoped
sys_appcatalog) - packages/services/service-tenant/src/services/app-catalog.service.ts - TS -> JSON compile pipeline.
objectstack compilevalidates a local TS stack againstObjectStackDefinitionSchemaand writesdist/objectstack.json- packages/cli/src/commands/compile.ts. - Zod -> JSON Schema publishing pipeline. The bridge that makes TS/JSON interchangeable - packages/spec/scripts/build-schemas.ts.
- Scaffolded TS file tree.
create-objectstackemits adefineStack()entry point plus split-outsrc/objects/*.ts,src/views/*.tsetc. - packages/create-objectstack/src/index.ts. - ObjectOS metadata DB bridge removed.
MetadataPluginno longer registerssys_metadata/sys_metadata_historyinto the ObjectOS manifest and no longer auto-connects ObjectQL to aDatabaseLoader; runtime metadata is hydrated from files or artifacts only - packages/metadata/src/plugin.ts. - JSON-payload storage primitive.
sys_metadata.metadataproves the platform can persist JSON payloads in metadata objects - packages/metadata/src/objects/sys-metadata.object.ts. In the version-first model, user metadata browsing can be a projection from Project Version artifacts rather than the primary storage path. - Project artifact envelope schema (M1).
ProjectArtifactSchemav0 exists withschemaVersion,projectId,commitId,checksum,metadata,functions,manifest, and reservedpayloadRef- packages/spec/src/cloud/project-artifact.zod.ts. - Project artifact path binding. Multi-project provisioning accepts
metadata.artifact_path, andos projects create --artifact/os projects bind --artifactwire local compiled bundles into existing projects - packages/cli/src/commands/projects/create.ts, packages/cli/src/commands/projects/bind.ts. - Live kernel bundle resolver. Project kernels read
sys_project.metadata.artifact_path/artifact_paths[], withOS_PROJECT_ARTIFACTSandOS_PROJECT_ARTIFACT_ROOToverrides for local development - apps/objectos/server/fs-app-bundle-resolver.ts. - Object identity is single-sourced on
name.ObjectSchemaBase.namespacehas been removed; package namespace remains internal registry metadata, not an object identity field - packages/spec/src/data/object.zod.ts. - Manifest scope enum trimmed.
ManifestSchema.scopeaccepts onlycloud,system, andproject- packages/spec/src/kernel/manifest.zod.ts. - Canonical package manifest files. Plugin/service packages now share a single
src/manifest.tsbetween compile-time config and runtime registration, reducing object-list drift. - CLI
publishlink. The end-to-end "local JSON -> remote server" wire is alive, even though the endpoint shape is wrong - packages/cli/src/commands/publish.ts. env_id→project_idmigration (M2 — superseded 2026-04). Metadata storage tables (sys_metadata,sys_metadata_history) were migrated fromenv_idtoproject_idas the partition key. Idempotent helper at@objectstack/metadata/migrations- packages/metadata/src/migrations/migrate-env-id-to-project-id.ts. Note: per the ADR-0008 §0 amendment (2026-04-13),projectandbranchare removed fromMetaRef;SysMetadataRepositorynow keys overlay rows purely byorganization_idand ignores the legacyproject_id/branchcolumns at read time. They survive in the schema for backward compatibility and will be dropped in a future major. (Per-type tablessys_object/sys_view/sys_flow/sys_agent/sys_toolwere removed in 2026-05 — all metadata now lives as JSON insys_metadata.)- Artifact API endpoints (M3 + D2).
GET /api/v1/cloud/projects/:id/artifactreturns the current artifact assembled fromsys_project.metadata.artifact_path.POST /api/v1/cloud/projects/:id/metadatareceives compiled JSON, persists it under<artifactRoot>/<projectId>/artifact.json, and updates the project metadata pointer — packages/services/service-cloud/src/cloud-artifact-api-plugin.ts.GET /api/v1/cloud/resolve-hostnameresolves a hostname to a project id. - CLI
publishroutes to project publish endpoint (D2).objectstack publishnow POSTs to/api/v1/cloud/projects/:id/metadata, accepts--projectand--serverflags, and returnsversionId,commitId, andchecksum— packages/cli/src/commands/publish.ts. apps/objectosuses ObjectOS runtime mode (M4).apps/objectos/objectstack.config.tsboots viacreateBootStack({ runtime: { cloudUrl: ... } }), separating the ObjectOS runtime from the control plane — apps/objectos/objectstack.config.ts.apps/cloudis an independent control plane (D8).apps/cloud/objectstack.config.tsboots viacreateBootStack({ mode: 'cloud', ... }), running the control plane independently without the ObjectOS runtime manifest — apps/cloud/objectstack.config.ts.- Storage driver matrix expanded to MongoDB.
@objectstack/driver-mongodbships a fullIDataDriver(filter translator, aggregation builder, schema sync) with 75 unit tests againstmongodb-memory-server, and is wired into the CLI /service-clouddriver factory — packages/plugins/driver-mongodb/, packages/services/service-cloud/src/driver-factory.ts. - Driver auto-detection from URL scheme.
OS_DATABASE_URL/OS_CONTROL_DATABASE_URLinfer the driver frommongodb://,postgres://,mysql://,libsql://,file:,:memory:so deploys can drop the explicitOS_DATABASE_DRIVERflag — packages/cli/src/commands/serve.ts, apps/objectos/objectstack.config.ts. - CLI startup banner shows resolved driver + redacted DB URL.
objectstack serveprints aDriver:line (e.g.MongoDBDriver → mongodb://admin:****@host/db) so operators can verify URL inference at a glance — packages/cli/src/utils/format.ts. - Multi-driver e2e harness. The HotCRM reference app ships a shared
runDriverAcceptance()helper plus per-driver Playwright specs (sqlite / mongodb / postgres) and ascripts/run-driver-acceptance.shwrapper that bootspnpm devper-driver. - AI routes are identity-aware end to end. Cookie sessions and Bearer tokens both resolve to
req.useron/api/v1/ai/*, which is forwarded astoolExecutionContext.actorso everyquery_records/get_record/aggregate_data/action_*tool call runs through ObjectQL with the caller's RLS context — agents inherit the user's row visibility for free. LLM-generated conversation titles also fire under that same identity. See AI Capabilities → Permission-aware execution.
Drift (Needs Cleanup)
- Half-wired abstractions:
ScopedServiceManagerandSharedProjectPluginwere added but their integration into the request path is incomplete. Either finish them or remove. ProjectKernelFactory直连控制面 DB 读sys_project/sys_project_credential,绕过 Artifact API。 packages/runtime/src/project-kernel-factory.ts 在 boot 时直接查控制面表拿业务 DB 坐标和凭据,破坏了 §8「ObjectOS 只通过 Artifact API 接触控制面」的边界。修复方向:业务 DB 坐标和凭据归 Deployment Config(§6.3 B),通过启动时注入而非控制面 DB 直连。- Custom Salesforce-flavor formula engine.
packages/objectql/src/formula.ts 是一个 433
LoC、22 函数、手写递归下降的私有 DSL。它没有公开训练语料,AI agent 每次都要从
prompt 里学一遍语法;
evaluateFormula用try { … } catch { return undefined }吞掉所有错误,业务规则静默失败;无递归深度上限/求值步数上限/超时,是潜在的 DoS 通道。修复方向:删除并替换为 CEL(Apache-2.0、Google CEL spec、K8s/GCP IAM/ Firestore 训练语料丰富、形式语法、AST-first、有界执行)。详见 ROADMAP M9。 - Untyped
z.string()expression fields scattered across spec. ~25 个formula / condition / expression / criteria / visible / visibleOn字段散布在 Data / UI / Security / Automation / AI / Kernel 各域,全部声明为裸z.string()并附.describe('Formula expression')。这些字段实际涉及多种语言 (Salesforce-style formula、CEL-style predicate、JS expression、cron、SQL、 OpenAPI runtime expression),但 schema 层既不区分 dialect 也不强制 AST 形式。 修复方向:统一替换为ExpressionSchema { dialect, ast }(详见 ROADMAP M9.3)。 - Compile-time-frozen seed timestamps.
HotCRM 的种子
记录用
new Date(Date.now() + 86400000 * 30)形式生成日期。这些表达式在 TS 编译期就被求值,把开发者的 wall-clock 烧进dist/objectstack.json:(1) 两次 连续objectstack build的产物不字节一致,破坏 cacheable artifact 的隐式 契约;(2) 客户后续安装得到的种子日期是开发者的「今天 + 30 天」,dynamic semantics 完全丢失。修复方向:种子记录里使用{ dialect: 'cel', ast: <os.now() + duration("P30D")> },由 SeedLoader 在 install 时按客户时钟求值。详见 ROADMAP M9.4。
Missing (Not Started)
- Project Version storage - persist every publish as an immutable Project Version with artifact JSON or
payloadRef,versionId,versionNumber,commitId, checksum, publish metadata, and summary indexes. - Current Version pointer - add a project-level pointer that decides which Version ObjectOS serves; rollback/promote should update this pointer without mutating Version rows.
- Studio version/artifact viewer - browse current version, publish history, artifact envelope, version id, commit id, checksum, logs, and runtime health. No metadata editing.
- UI auto-generation - artifact schemas -> Amis/React components without hand-wiring.
- Unified
ExpressionSchema+ AST-first persistence. Single{ dialect, ast }envelope replacing all scatteredz.string()formula fields. Persisted artifact contains AST only; source strings are an authoring-time DX shorthand normalized away byobjectstack compile. See ROADMAP M9.1 / M9.2 / M9.3. packages/formula+ ObjectStack CEL stdlib. New package wrappingcel-jswith a registry (cel/js/crondialects) and an ObjectStack standard function namespace (os.now(),os.today(),os.user.*,os.org.*,os.exists(),os.lookup(), etc.). See ROADMAP M9.1.- AI structured-output for expressions. Publish
CelExprSchemaas JSON Schema, mandate AST-only output inskills/objectstack-formula/SKILL.md, wire constrained decoding into Studio AI assistant + CLI scaffolding. See ROADMAP M9.7.
8. Non-Goals and Anti-Patterns
Decisions the platform has already rejected for Phase 1. Do not relitigate without explicit project-level buy-in.
Phase 1 non-goals
- No branches in this round.
sys_branch,branch_id, branch hostnames, branch diff/merge, branch DB forks, and branch-scoped publish endpoints are deferred. Versioning comes first; branches later become named runnable pointers to versions. - Studio does not edit user metadata in this round. It is a dashboard, version history, artifact inspector, and observability surface.
- No bidirectional CLI/Studio write model. The local TS workspace is the only Phase 1 metadata authoring source.
- No
objectstack pullin this round. Reverse-hydrating JSON back into TS is deferred until Studio or other control-plane writers exist. - No merge/conflict UX in this round.
versionIdandcommitIdidentify versions and artifacts; they are not yet a collaborative merge protocol. - No multi-environment release train in this round. Phase 1 supports one current Version per Project. Staged promotion across dev/staging/prod and branch-specific releases are deferred.
- No S3 in this round. The Artifact API's backend is the control-plane DB today. Design the response shape to accommodate a future
{ url, expiresAt, checksum }indirection without building the S3 path now.
Architectural boundaries
- ObjectOS runtimes don't talk to the control-plane DB. The only coupling surface is the Artifact API (HTTP). Runtimes never open a connection to the control-plane database, even for "just a quick lookup."
- Project business data is not metadata. Business rows live in the per-project business DB. Metadata lives in the control plane.
- Deployment Config 不进 artifact。 业务 DB 坐标、凭据、进程密钥、租户身份等可变运行配置必须从启动环境(云端模式:控制面
sys_project+ env;本地模式:扁平 env)注入,绝不写入objectstack.json。把可缓存的不可变内容和易变的部署配置混在一起会同时破坏 §2 和 §8。详见 §6.3。
Representation-form boundaries
- JSON is not an authoring form. Phase 1 authors produce metadata via TypeScript and the CLI. If you find yourself editing Project Version JSON directly in the control plane, you are patching around a missing CLI affordance.
- TS is not a wire form. ObjectOS runtimes never consume
.tsfiles directly. The only runtime inputs are a compileddist/objectstack.jsonor the control plane's Artifact API response (also JSON). - No bypassing Zod for TS-to-JSON conversion. Any hand-rolled serializer will diverge from the schema on the Nth protocol bump and silently corrupt data.
z.toJSONSchema()+schema.parse()is the contract.
Prior anti-patterns (still in force)
- No escape hatch for non-metadata logic. All business logic lives in declared Object Functions or Flows. "Just add a raw endpoint" is not a supported pattern.
-
packages/specstays logic-free. Schemas, types, constants only. Runtime logic belongs incore/runtime/services. (Prime Directive #2.) - No
namespaceprefix mechanism. If a module needs a prefix, embed it in the objectname(sys_user,crm_account). The field is deprecated. (Prime Directive #7.) - No direct-DB SDK. Every client access goes through ObjectQL - no bypass, no "just for perf" exception.
- No deprecated-alias enums. When refactoring enums (plugin scope, metadata types, etc.), break cleanly with a migration. Do not ship "old value still accepted" aliases.
- No hand-written content in
content/docs/references/. That tree is regenerated from Zod schemas. - No private expression DSL. Any "expression" string in metadata MUST declare a
dialectand persist as an AST. New private grammars (Salesforce-flavor formula, custom predicate languages, ad-hoc condition syntax) are forbidden — they have no public training corpus, no formal grammar, and no AST round-trip story, all of which are required for the AI-native authoring path. The canonical dialects arecel(formulas + predicates),js(sandboxed L2 hook bodies), andcron(job schedules). SQL fragments stay driver-native and are not part of the expression registry. - Artifacts must be deterministic.
objectstack compileof unchanged source MUST produce a byte-identicalobjectstack.json. Anything that drifts between two consecutive builds — wall-clock timestamps,Date.now()/Math.random()/ process IDs / absolute machine paths inside seed values, hostname-dependent data — is a bug. Dynamic semantics (e.g. "30 days from install time") belong inExpressionSchemaAST, evaluated at install time by the runtime, not at compile time by the author's clock. CI gate (M9.6):objectstack buildtwice,sha256must match.
9. Open Questions
Questions this document deliberately leaves unresolved. Answer them in follow-up design docs before building.
Artifact content format details.Resolved (2026-04-26):ProjectArtifactSchemav0 defines the envelope, manifest requirements, function-code packaging, checksum, and futurepayloadRefindirection.Business DB connection config ownership.Resolved (2026-04-25): Business DB coordinates and credentials are Deployment Config, never part of the Artifact. See §6.3 Runtime Inputs. Cloud mode sources them from control-planesys_project/sys_project_credential+ process env; local single-project mode sources them from flatOS_*env vars.- Artifact cache invalidation. Control-plane push notification? Periodic polling from ObjectOS? HTTP ETag on every request? Each has latency / chattiness / outage-resilience trade-offs.
- Custom domains. Customers bringing their own domain (
app.acme.com) - how does it map to project, and where is the TLS cert issued? - Local
devmetadata form. Doesobjectstack devlet ObjectOS consume the in-memory TS definition directly (best DX: hot reload is trivial), or does it always go through acompilestep to JSON first (best isomorphy with prod: fewer "works on my machine" bugs)? - Future Studio editing. When Studio becomes a metadata authoring surface, should it write through a control-plane REST API only? How does it preserve round-trip expectations with TS-authored projects?
- Future branch model. When branches ship, should each branch be a named runnable pointer to a Project Version with its own hostname, business DB reference, and deployment config? How should branch creation fork or clone data from the source Version?
- Future
objectstack pullfidelity. Is a lossless JSON -> TS emitter feasible for everything users might write indefineStack()? If authors use TS-only expressions, helper imports, or generics, the pull step either loses information or must constrain the authoring surface. - Project Version promotion semantics. Does
publishalways auto-promote the new Version tocurrent_version_id, or should the control plane support staged versions where publish and promote are separate operations?
When in doubt, re-read this. Every PR description, every commit, every plan should be testable against this document: does this change preserve Built, reduce Drift, unlock Missing, or answer an Open Question? If none, it probably shouldn't ship.