-
8cf4f7c: fix(runtime): mount
GET /readyso the readiness probe is reachable over HTTPThe dispatcher's
/readybranch (seam #2) was only reachable when callingdispatch()directly — noserver.get('${prefix}/ready')registration existed, so a real server returned the Hono not-found 404 before the handler ran (the same class of bug as/mcpand/keys)./readyis now mounted alongside/health, returning 200 while the kernel isrunningand 503 while it is booting or draining — the contract the EE multi-node rolling-restart drain gate polls (cloud ADR-0018). Adds a registration assertion plus an integration test that hits the endpoint through a real HTTP server. -
f2063f3: fix(cli): extend native better-sqlite3 → wasm SQLite auto-fallback to the persistent-file /
--artifactdev path (#2229)The native-
better-sqlite3→ wasm SQLite → in-memory step-down previously only guarded the zero-config:memory:dev branch ofserve. A normalobjectstack devrun never reaches it —devinjects a persistentfile:DB (so AI-authored data survives restarts) and--artifactboots resolve sqlite through the datasource factory — both of which constructedbetter-sqlite3directly with no probe and no fallback. An ABI mismatch (e.g. a cached prebuilt binary built for a different Node version) was therefore not caught at boot and surfaced later as a runtimeFind operation failedon the first query.The probe-by-connect + step-down is now hoisted into a shared
resolveSqliteDriverhelper (@objectstack/service-datasource) and applied to both previously-unguarded sqlite construction sites: the explicitsqlite/file:branch inserve.tsand the sqlite branch of the default datasource driver factory. better-sqlite3 loads its native addon lazily (first query), so the helper forces the load with aSELECT 1and, in dev only, steps down to wasm SQLite (real SQL + on-disk persistence — the samefile:keeps working) then to the in-memory driver as a last resort, emitting the existing⚠ native better-sqlite3 unavailable …warning. In production the native driver is returned unprobed so a load failure surfaces loudly (fail-closed) rather than silently degrading to a different engine. -
Updated dependencies [2b355d5]
-
Updated dependencies [5ba52b0]
-
Updated dependencies [211425e]
-
Updated dependencies [f2063f3]
- @objectstack/service-cluster@10.3.0
- @objectstack/driver-sql@10.3.0
- @objectstack/objectql@10.3.0
- @objectstack/service-datasource@10.3.0
- @objectstack/driver-sqlite-wasm@10.3.0
- @objectstack/spec@10.3.0
- @objectstack/core@10.3.0
- @objectstack/types@10.3.0
- @objectstack/metadata@10.3.0
- @objectstack/observability@10.3.0
- @objectstack/formula@10.3.0
- @objectstack/rest@10.3.0
- @objectstack/driver-memory@10.3.0
- @objectstack/plugin-auth@10.3.0
- @objectstack/plugin-org-scoping@10.3.0
- @objectstack/plugin-security@10.3.0
- @objectstack/service-i18n@10.3.0
- Updated dependencies [b496498]
- @objectstack/spec@10.2.0
- @objectstack/core@10.2.0
- @objectstack/formula@10.2.0
- @objectstack/metadata@10.2.0
- @objectstack/objectql@10.2.0
- @objectstack/observability@10.2.0
- @objectstack/driver-memory@10.2.0
- @objectstack/driver-sql@10.2.0
- @objectstack/driver-sqlite-wasm@10.2.0
- @objectstack/plugin-auth@10.2.0
- @objectstack/plugin-org-scoping@10.2.0
- @objectstack/plugin-security@10.2.0
- @objectstack/rest@10.2.0
- @objectstack/service-cluster@10.2.0
- @objectstack/service-datasource@10.2.0
- @objectstack/service-i18n@10.2.0
- @objectstack/types@10.2.0
-
ac79f16: feat(datasource): auto-connect declared external datasources (ADR-0062 Phase 1, D1/D2/D5)
A declared external datasource is now connected to a live ObjectQL driver and its federated objects are queryable with zero app code — no
onEnabledriver wiring. Implements ADR-0062 Phase 1.- D1 — one connect path. New
DatasourceConnectionServicein@objectstack/service-datasourceowns the single "definition → live driver" path: build via the injected driver factory → resolveexternal.credentialsRefvia theSecretBinder→ connect →engine.registerDriverunder the datasource name → register the datasource def → sync each bound federated object's read metadata (DDL-free). Both origins converge on it: the runtime-adminregisterPoolnow delegates here, andAppPluginauto-connects code-defined datasources. Exposed as the'datasource-connection'kernel service. - D2 — opt-in-safe gate. A declared datasource auto-connects only when it is
external, an object explicitly binds to it viaobject.datasource, or it sets the newautoConnect: trueflag. A managed datasource that nothing explicitly binds (incl. ones referenced only by adatasourceMappingrule, e.g.examples/app-crm's:memory:datasources) stays metadata-only — existing apps are byte-for-byte unchanged. See the ADR-0062 D2 implementation note. - D5 — lifecycle, ordering & policy. Connect happens in
AppPlugin.start()(before thekernel:readyvalidation gate, relying on the kernel's init-all-then-start-all ordering). Fail-fast for a declaredexternaldatasource withvalidation.onMismatch: 'fail'; degrade-with-warning otherwise (and always for runtime-admin/rehydrate, so a UI action or replica blip never bricks the server). Adds a host-injectableDatasourceConnectPolicy(open-core default allows; a multi-tenant host binds a stricter fail-closed policy for egress isolation) consulted before every connect — one connect path, no cloud fork.
Adds
datasource.autoConnectto the spec. The legacyonEnable+ctx.drivers.registerbridge remains supported as an escape hatch (idempotent vs. auto-connect). No behavior change for managed apps. - D1 — one connect path. New
-
94d2161: refactor(runtime): build the standalone default driver via the shared datasource factory (ADR-0062 follow-up)
createStandaloneStacknow constructs itsdefaultdriver for the user-facing kinds (memory / better-sqlite3 / postgres / mongodb) through the samecreateDefaultDatasourceDriverFactoryused for declared and runtime-admin datasources — one "driver kind → instance" construction path instead of two hand-mirrored ones. Adding a dialect or changing connection/pool defaults now happens in a single place. URL→config translation, filesystem prep (mkdir), and pre-engineDriverPluginregistration stay in the stack (unchanged); the factory only constructs the driver. The pure-JS WASM sqlite driver stays bespoke in the stack — it's the standalone-specific, CI-safe default and not a user-creatable datasource type, so it has a single construction site already.No behavior change: the same driver instances are built for the same inputs (verified by a per-kind connect + CRUD round-trip test and a real
os devboot). Adds@objectstack/service-datasourceas a runtime dependency (no cycle — that package depends only on core/spec). -
Updated dependencies [49da36e]
-
Updated dependencies [49da36e]
-
Updated dependencies [ac79f16]
-
Updated dependencies [517dad9]
- @objectstack/spec@10.1.0
- @objectstack/service-datasource@10.1.0
- @objectstack/driver-sql@10.1.0
- @objectstack/rest@10.1.0
- @objectstack/core@10.1.0
- @objectstack/formula@10.1.0
- @objectstack/metadata@10.1.0
- @objectstack/objectql@10.1.0
- @objectstack/observability@10.1.0
- @objectstack/driver-memory@10.1.0
- @objectstack/driver-sqlite-wasm@10.1.0
- @objectstack/plugin-auth@10.1.0
- @objectstack/plugin-org-scoping@10.1.0
- @objectstack/plugin-security@10.1.0
- @objectstack/service-cluster@10.1.0
- @objectstack/service-i18n@10.1.0
- @objectstack/types@10.1.0
-
e16f2a8: BREAKING: the system object
sys_departmentis renamed tosys_business_unit— object + member table (sys_department_member→sys_business_unit_member), fields, and i18n — with no compatibility alias. Any deployment holdingsys_departmentrows, or metadata that references the object by name (lookups, list views, queries, sharing/approval scopes), must migrate tosys_business_unit. A renamed shipped system object is a breaking change to the platform's public data surface, so this lands as a major. Verified per ADR-0059's pre-publish hotcrm gate: no published downstream consumer references the old name.ADR-0057 — ERP authorization core. Adds permission-grant access DEPTH (
own/own_and_reports/unit/unit_and_below/org), renamessys_department→sys_business_unit(no aliases — see BREAKING above), introduces the platform-ownedsys_user_roleassignment, and seeds stack-declaredroles/sharingRulesintosys_role/sys_sharing_ruleat boot (closes #2077). Hierarchy-relative scopes are delegated to a pluggableIHierarchyScopeResolver(open edition fails closed to owner-only;defineStackerrors withoutrequires: ['hierarchy-security']). Also fixes a latent over-grant whereengine.find({ filter })was ignored (driver readswhere) — normalizedfilter→wherein the engine. -
220ce5b: Resolve the tenant default currency onto ExecutionContext.
Adds
ExecutionContext.currency(ISO 4217) and resolves it from thelocalization.currencysetting alongsidetimezone/locale— in both the runtimeresolveExecutionContextand the REST mirror. This is the foundation for the documented "applied when a currency field omits its own" fallback: the tenant default is now carried on every request context, so analytics enrichment, formatters, and renderers can resolve a measure/field currency down to the org default instead of hard-coding it. Undefined when no tenant default is configured (consumers then render a plain number).
-
47d978a: Fix: the artifact-serve path now honors an app-declared default permission-set profile (
isProfile: true, isDefault: true) underobjectstack dev/serve/start.createStandaloneStack(the boot path used when serving a compileddist/objectstack.jsonwith no hostobjectstack.config.ts) surfacedobjects/requires/manifestfrom the artifact bundle but droppedpermissions[]androles[]. As a result the CLI'sappDefaultProfileName(config.permissions)sawundefinedand the SecurityPlugin fell back to the built-in owner-onlymember_default— so an app whose default profile carries e.g.readScope: 'unit_and_below'(ADR-0056 D7 / ADR-0057 D1) was silently ignored. The config-load path was unaffected because the app'spermissionssurvived via the original stack object.createStandaloneStacknow surfacespermissions[]androles[]from the artifact bundle, mirroring the existingobjects/requires/manifesthandling, so the artifact-serve path applies the app default profile exactly like the config-load path. -
Updated dependencies [d7ff626]
-
Updated dependencies [92db3e5]
-
Updated dependencies [2a1b16b]
-
Updated dependencies [2256e93]
-
Updated dependencies [e16f2a8]
-
Updated dependencies [cfd86ce]
-
Updated dependencies [e411a82]
-
Updated dependencies [a581385]
-
Updated dependencies [d5f6d29]
-
Updated dependencies [220ce5b]
-
Updated dependencies [3efe334]
-
Updated dependencies [3754f80]
-
Updated dependencies [feead7e]
-
Updated dependencies [6ca20b3]
-
Updated dependencies [5f875fe]
-
Updated dependencies [b469950]
-
Updated dependencies [48a307a]
-
Updated dependencies [25fc0e4]
- @objectstack/spec@10.0.0
- @objectstack/driver-sql@10.0.0
- @objectstack/objectql@10.0.0
- @objectstack/rest@10.0.0
- @objectstack/plugin-security@10.0.0
- @objectstack/formula@10.0.0
- @objectstack/core@10.0.0
- @objectstack/metadata@10.0.0
- @objectstack/observability@10.0.0
- @objectstack/driver-memory@10.0.0
- @objectstack/driver-sqlite-wasm@10.0.0
- @objectstack/plugin-auth@10.0.0
- @objectstack/plugin-org-scoping@10.0.0
- @objectstack/service-cluster@10.0.0
- @objectstack/service-i18n@10.0.0
- @objectstack/types@10.0.0
-
2afb612: feat(security): resolve
current_user.emailin RLS owner policiesRLS
usingpredicates can now referencecurrent_user.email— a unique, human-readable, seedable owner anchor (owner = current_user.email). Previously the RLS compiler resolved onlycurrent_user.id/organization_id/roles/org_user_ids, so any owner-by-name/email predicate silently compiled to the deny sentinel (fail-closed → the user saw nothing). Email is sourced for free from the auth session (with a boundedsys_userfallback for the API-key path) and threaded onto theExecutionContextin both identity resolvers — the REST data path (rest-server) and the dispatcher path (resolve-execution-context).Display
nameis deliberately not exposed to RLS: names collide, and a collision on an ownership predicate is an access-control leak. Only unique identifiers (id,email) are resolvable.This makes owner-scoped row-level security work with seed data (no per-user ids needed) and, combined with
controlled_by_parent(ADR-0055), lets a master's owner scoping flow to its detail records. The example-showcase demonstrates it:showcase_invoicecarries anowneremail + an owner RLS policy, its lines are controlled-by-parent, and invoices/lines are seeded per owner. It also fixes the showcase's previously inert owner predicates (they used==andcurrent_user.name, neither of which the compiler accepts) to= current_user.email. -
Updated dependencies [e7f6539]
-
Updated dependencies [e7f6539]
-
Updated dependencies [fa8964d]
-
Updated dependencies [2365d07]
-
Updated dependencies [6595b53]
-
Updated dependencies [fa8964d]
-
Updated dependencies [751f5cf]
-
Updated dependencies [5a5a9fe]
-
Updated dependencies [36138c7]
-
Updated dependencies [a8e4f3b]
-
Updated dependencies [4c213c2]
-
Updated dependencies [2afb612]
- @objectstack/spec@9.11.0
- @objectstack/rest@9.11.0
- @objectstack/plugin-security@9.11.0
- @objectstack/objectql@9.11.0
- @objectstack/driver-sql@9.11.0
- @objectstack/core@9.11.0
- @objectstack/formula@9.11.0
- @objectstack/metadata@9.11.0
- @objectstack/observability@9.11.0
- @objectstack/driver-memory@9.11.0
- @objectstack/driver-sqlite-wasm@9.11.0
- @objectstack/plugin-auth@9.11.0
- @objectstack/plugin-org-scoping@9.11.0
- @objectstack/service-cluster@9.11.0
- @objectstack/service-i18n@9.11.0
- @objectstack/types@9.11.0
-
1f88fd9: Add a transaction boundary to sandboxed hook/action bodies:
ctx.api.transaction(async () => { … }). Everyctx.apiread/write inside the callback runs in one driver transaction — committed when the callback returns, rolled back if it throws (or if the body leaves the transaction open at timeout). Guarded by the newapi.transactioncapability.- spec: new
api.transactioncapability token onHookBodyCapability. - objectql:
ScopedContextgains discretebeginTransaction()/commitTransaction(handle)/rollbackTransaction(handle)primitives. The handle is threaded explicitly through a child context (resolveTxhonors it ahead of the ambienttxStore), because the sandbox drives the body across many host event-loop turns where AsyncLocalStorage context does not survive. Degrades to non-transactional execution when the driver has no transaction support. - runtime: the QuickJS runner wires
ctx.api.transactionover three deferred-promise host leaves (begin/commit/rollback), routes in-transaction ops through the tx-scoped context, and rolls back a transaction the body left open before disposing the VM.
- spec: new
-
e2b5324: feat(ownership): auto-provision a canonical
owner_idand hand seeded records to the first adminOwnership is now correct-by-default instead of opt-in — closing the gap where seeded demo data ended up owned by nobody a human can log in as (so "My" views, owner reports and owner notifications were empty out of the box) and where author-written objects silently shipped with no working ownership at all.
-
applySystemFields(objectql) now auto-injects a canonical, reassignableowner_idlookup (→sys_user) on user-authored business objects, alongside the existing tenant/audit fields. Unlike the audit*_bylookups it is NOT readonly — ownership transfers. Withheld formanagedBy/sys_*tables and for objects that opt out viaownership: 'org' | 'none'(Dataverse-style). The safe default direction: forgetting the opt-out leaves a harmless spare column, whereas the old opt-IN model let authors ship objects with broken ownership. Once present, the existing machinery engages automatically (insert auto-stamp, owner-scoped RLS, owner-keyed views/reports). -
claimSeedOwnership(plugin-security), invoked frombootstrapPlatformAdminright after the first human is promoted to platform admin, transfers ownership of seeded rows (owner_idNULL orusr_system) to that admin. The ownership twin of org-scoping'sclaimOrphanOrgRows. Idempotent; skipsmanagedBy/sys_*. Authors write plain seed records (noowner_id) and the platform — not the author — performs the handoff, so there is nothing to remember or mistype. -
usr_systemis never minted (runtime + objectql). The seed loader bindsos.userto a NULL identity, socelos.user.id``resolves to NULL at seed time (the owning admin does not exist yet) and the row seeds NULL-owned — then the handoff above fills it. The runtime'sensureSeedIdentity(the only code that inserted a`usr_system`row) is removed.`SystemUserId.SYSTEM`survives only as a reserved id so legacy DBs' exclusion guards / ownership handoff still recognize a pre-existing row.`os.org`is unaffected (derived from`organizationId`).
Also hardens
bootstrapPlatformAdminagainst a latent dts typecheck error (defensive read of the untypeddescriptionon seed permission sets). -
- Updated dependencies [db02bd5]
- Updated dependencies [641675d]
- Updated dependencies [d9508d1]
- Updated dependencies [1d352d3]
- Updated dependencies [1f88fd9]
- Updated dependencies [94e9040]
- Updated dependencies [f169558]
- Updated dependencies [1f88fd9]
- Updated dependencies [1f88fd9]
- Updated dependencies [e2b5324]
- Updated dependencies [fd07027]
- @objectstack/driver-sql@9.10.0
- @objectstack/spec@9.10.0
- @objectstack/formula@9.10.0
- @objectstack/plugin-org-scoping@9.10.0
- @objectstack/plugin-security@9.10.0
- @objectstack/objectql@9.10.0
- @objectstack/rest@9.10.0
- @objectstack/driver-sqlite-wasm@9.10.0
- @objectstack/core@9.10.0
- @objectstack/metadata@9.10.0
- @objectstack/observability@9.10.0
- @objectstack/driver-memory@9.10.0
- @objectstack/plugin-auth@9.10.0
- @objectstack/service-cluster@9.10.0
- @objectstack/service-i18n@9.10.0
- @objectstack/types@9.10.0
- @objectstack/spec@9.9.1
- @objectstack/core@9.9.1
- @objectstack/types@9.9.1
- @objectstack/metadata@9.9.1
- @objectstack/objectql@9.9.1
- @objectstack/observability@9.9.1
- @objectstack/formula@9.9.1
- @objectstack/rest@9.9.1
- @objectstack/driver-memory@9.9.1
- @objectstack/driver-sql@9.9.1
- @objectstack/driver-sqlite-wasm@9.9.1
- @objectstack/plugin-auth@9.9.1
- @objectstack/plugin-org-scoping@9.9.1
- @objectstack/plugin-security@9.9.1
- @objectstack/service-cluster@9.9.1
- @objectstack/service-i18n@9.9.1
-
11af299: feat(runtime): resolve a reference timezone onto ExecutionContext (ADR-0053 Phase 2 foundation)
Adds
ExecutionContext.timezone(optional IANA zone) and resolves it once per request inresolveExecutionContext, with precedence user preference → org default →UTC:- User override:
sys_user_preferencerow(user_id, key='timezone'). - Org default: the tenant-scoped
sys_setting(namespace='localization', key='timezone', scope='tenant')— one org per physical tenant (ADR-0002), so no tenant_id filter is needed. - An invalid IANA zone is ignored and resolution falls through; every read is defensive and never blocks auth.
This is pure plumbing with no behavior change: nothing reads
ctx.timezoneyet, and an absent value resolves toUTC(today's behavior). It is the foundation the rest of ADR-0053 Phase 2 consumes — tz-awaretoday()/daysFromNow()(#1980), datetime rendering (#1981), and analytics bucketing (#1982). A discoverablelocalizationsettings manifest for the org default is a follow-up; the resolver already reads the row if present.Part of #1978.
- User override:
-
9afeb2d: feat(settings):
localizationsettings — platform default timezone, language & formats (ADR-0053 Phase 2)Adds a
localizationSettingsManifest, the missing keystone that makes the Phase 2 reference-timezone actually configurable end-to-end. One declaration gives the full settings stack for free: platform built-in default →global→tenantcascade, a permission-gated settings page, and i18n.Keys (organization-level; per-user overrides intentionally out of scope for v1):
timezone(UTC),locale(en-US),default_country,date_format,time_format,number_format,first_day_of_week,currency(USD),fiscal_year_start. Benchmarked against Salesforce/Workday "Company Information + Locale".Resolver 收编 —
resolveExecutionContextnow resolvestimezoneandlocalefrom thelocalizationsettings via thesettingsservice (canonical 4-tier cascade), falling back to a direct tenant-scopedsys_settingread, thenUTC/en-US. This replaces the hand-rolledsys_user_preference+ tenant-onlysys_settingpath from #1978 (which bypassed the settings abstraction and is dropped along with the per-user tier). NewExecutionContext.locale.Consumer wiring — analytics date bucketing now picks up the resolved org timezone:
DatasetExecutorthreadsExecutionContext.timezoneinto the query (precedence: explicit selection tz → request tz → UTC), so #1982's tz-aware buckets fire for a configured org without callers passing a zone. Formulatoday()/datetimewere already wired (#1979/#1980).Email
datetimerendering (SendTemplateInput.timezone, shipped in #1981) is intentionally not wired here: the only currentsendTemplatecallers are pre-session auth emails with no org context; business-notification callers can pass the zone when they appear.
-
83fd318: fix(runtime): drive sandbox host calls with deferred promises and a deadline-bounded pump
The QuickJS sandbox exposed
ctx.api.object(x).find/update/...vianewAsyncifiedFunction, which unwinds the WASM stack per host call and forbids a second call while the first is unwound. A script awaiting two host calls in sequence (e.g. an action doingfindOne()thenupdate()) drove the second call from a continuation resumed insideexecutePendingJobs, corrupting the wasm heap (memory access out of bounds/p->ref_count == 0) or exhausting the fixed 1000-iteration pump budget — surfacing asaction '…' did not resolve after 1000 pump iterations.Host API methods are now exposed as deferred QuickJS promises (
vm.newPromise()), so sequentialawaits compose with no stack unwinding, and the pump loop is bounded by the configuredtimeoutMsinstead of a fixed iteration cap. Host method calls now requireawait(the.object(name)proxy getter stays synchronous); a stuck/never-settling host call is cut off with a clear timeout error. -
Updated dependencies [84249a4]
-
Updated dependencies [0d4e3f3]
-
Updated dependencies [44c5348]
-
Updated dependencies [796f0d6]
-
Updated dependencies [11af299]
-
Updated dependencies [d5774b5]
-
Updated dependencies [bfa3102]
-
Updated dependencies [134043a]
-
Updated dependencies [67c29ee]
-
Updated dependencies [90108e0]
-
Updated dependencies [9afeb2d]
-
Updated dependencies [6bec07e]
-
Updated dependencies [92d75ca]
-
Updated dependencies [601cc11]
-
Updated dependencies [d99a75a]
-
Updated dependencies [575448d]
- @objectstack/spec@9.9.0
- @objectstack/plugin-auth@9.9.0
- @objectstack/objectql@9.9.0
- @objectstack/rest@9.9.0
- @objectstack/driver-sql@9.9.0
- @objectstack/plugin-security@9.9.0
- @objectstack/core@9.9.0
- @objectstack/formula@9.9.0
- @objectstack/metadata@9.9.0
- @objectstack/observability@9.9.0
- @objectstack/driver-memory@9.9.0
- @objectstack/driver-sqlite-wasm@9.9.0
- @objectstack/plugin-org-scoping@9.9.0
- @objectstack/service-cluster@9.9.0
- @objectstack/service-i18n@9.9.0
- @objectstack/types@9.9.0
- Updated dependencies [c17d2c8]
- Updated dependencies [7fe0b91]
- Updated dependencies [76ac582]
- Updated dependencies [97c55b3]
- Updated dependencies [1b1f490]
- Updated dependencies [884bf2f]
- @objectstack/formula@9.8.0
- @objectstack/rest@9.8.0
- @objectstack/objectql@9.8.0
- @objectstack/spec@9.8.0
- @objectstack/core@9.8.0
- @objectstack/metadata@9.8.0
- @objectstack/observability@9.8.0
- @objectstack/driver-memory@9.8.0
- @objectstack/driver-sql@9.8.0
- @objectstack/driver-sqlite-wasm@9.8.0
- @objectstack/plugin-auth@9.8.0
- @objectstack/plugin-org-scoping@9.8.0
- @objectstack/plugin-security@9.8.0
- @objectstack/service-cluster@9.8.0
- @objectstack/service-i18n@9.8.0
- @objectstack/types@9.8.0
- Updated dependencies [82c7438]
- Updated dependencies [417b6ac]
- Updated dependencies [ff0a87a]
- @objectstack/formula@9.7.0
- @objectstack/objectql@9.7.0
- @objectstack/spec@9.7.0
- @objectstack/core@9.7.0
- @objectstack/types@9.7.0
- @objectstack/metadata@9.7.0
- @objectstack/observability@9.7.0
- @objectstack/rest@9.7.0
- @objectstack/driver-memory@9.7.0
- @objectstack/driver-sql@9.7.0
- @objectstack/driver-sqlite-wasm@9.7.0
- @objectstack/plugin-auth@9.7.0
- @objectstack/plugin-org-scoping@9.7.0
- @objectstack/plugin-security@9.7.0
- @objectstack/service-cluster@9.7.0
- @objectstack/service-i18n@9.7.0
-
71578f2: feat(book): documentation navigation as a
bookelement — spine + derived membership (ADR-0046 §6)Adds the
bookmetadata element: a navigation spine (ordered groups +audience+ identity) whose membership is derived by rule (includeglob/tag) plus optional per-docorder/group, never a central array. This keeps AI authoring create-and-forget (no central-array read-modify-write) and runtime overlay merge-safe (RFC 7396 treats arrays atomically).BookSchema+resolveBookTree()derived-membership resolver +defineBook()+ additivedoc.order/doc.group.- Register
bookas a render-time metadata type (allowOrgOverride: true); wire it through the runtime type enumerations (PLURAL_TO_SINGULAR, engine registration, artifact field map, type-schema map). - REST
GET /meta/book/:name/treeresolves the tree; read-layeraudiencegating (public≡ anonymous;org/{profile}require sign-in).
-
Updated dependencies [d1e930a]
-
Updated dependencies [1b82b64]
-
Updated dependencies [71578f2]
-
Updated dependencies [bb00a50]
-
Updated dependencies [5e3a301]
-
Updated dependencies [5db2742]
-
Updated dependencies [b04b7e3]
-
Updated dependencies [d13df3f]
- @objectstack/spec@9.6.0
- @objectstack/plugin-auth@9.6.0
- @objectstack/objectql@9.6.0
- @objectstack/rest@9.6.0
- @objectstack/formula@9.6.0
- @objectstack/core@9.6.0
- @objectstack/metadata@9.6.0
- @objectstack/observability@9.6.0
- @objectstack/driver-memory@9.6.0
- @objectstack/driver-sql@9.6.0
- @objectstack/driver-sqlite-wasm@9.6.0
- @objectstack/plugin-org-scoping@9.6.0
- @objectstack/plugin-security@9.6.0
- @objectstack/service-cluster@9.6.0
- @objectstack/service-i18n@9.6.0
- @objectstack/types@9.6.0
- Updated dependencies [ee72aae]
- @objectstack/spec@9.5.1
- @objectstack/core@9.5.1
- @objectstack/formula@9.5.1
- @objectstack/metadata@9.5.1
- @objectstack/objectql@9.5.1
- @objectstack/observability@9.5.1
- @objectstack/driver-memory@9.5.1
- @objectstack/driver-sql@9.5.1
- @objectstack/driver-sqlite-wasm@9.5.1
- @objectstack/plugin-auth@9.5.1
- @objectstack/plugin-org-scoping@9.5.1
- @objectstack/plugin-security@9.5.1
- @objectstack/rest@9.5.1
- @objectstack/service-cluster@9.5.1
- @objectstack/service-i18n@9.5.1
- @objectstack/types@9.5.1
- Updated dependencies [d08551c]
- Updated dependencies [707aeed]
- Updated dependencies [7a103d4]
- Updated dependencies [4b01250]
- @objectstack/spec@9.5.0
- @objectstack/rest@9.5.0
- @objectstack/core@9.5.0
- @objectstack/formula@9.5.0
- @objectstack/metadata@9.5.0
- @objectstack/objectql@9.5.0
- @objectstack/observability@9.5.0
- @objectstack/driver-memory@9.5.0
- @objectstack/driver-sql@9.5.0
- @objectstack/driver-sqlite-wasm@9.5.0
- @objectstack/plugin-auth@9.5.0
- @objectstack/plugin-org-scoping@9.5.0
- @objectstack/plugin-security@9.5.0
- @objectstack/service-cluster@9.5.0
- @objectstack/service-i18n@9.5.0
- @objectstack/types@9.5.0
-
0856476: feat(metadata): package-scoped single-item resolution via
?package=(ADR-0048)A single-item metadata GET (
/meta/:type/:name?package=<id>) now resolves package-scoped (prefer-local): when two installed packages ship an item of the sametype/name, the requester's own package wins. Previously only the list endpoint was package-aware; a single-item fetch was context-free, so a cross-package collision always resolved to whichever package registered first.The fix threads
packageIdend-to-end:@objectstack/rest— the cacheable single-item path calledgetMetaItemCached(ETag keyed on type+name only) and dropped?package=. A?package=read now bypasses that cache and takes the disambiguatinggetMetaItem(type, name, packageId)path, so two same-named items never share one cache entry.@objectstack/objectql—protocol.getMetaItemforwardspackageIdto the overlay query (sys_metadata.package_id),MetadataFacade.get, andregistry.getItem;MetadataFacade.getgained an optionalcurrentPackageId.@objectstack/runtime— the parallel HTTP dispatcher threads?package=too.
This lets the doc viewer (
/apps/:packageId/docs/:name) resolve one doc scoped to its app, sodocnames no longer need a namespace prefix for uniqueness (the prefix becomes a recommended convention, likepage/dashboard/report);doc.zoddoc-comments updated accordingly.
- Updated dependencies [060467a]
- Updated dependencies [2c8e607]
- Updated dependencies [c1dfe34]
- Updated dependencies [0856476]
- Updated dependencies [fef38ec]
- Updated dependencies [3e675f6]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- @objectstack/spec@9.4.0
- @objectstack/metadata@9.4.0
- @objectstack/objectql@9.4.0
- @objectstack/rest@9.4.0
- @objectstack/driver-sql@9.4.0
- @objectstack/core@9.4.0
- @objectstack/formula@9.4.0
- @objectstack/observability@9.4.0
- @objectstack/driver-memory@9.4.0
- @objectstack/driver-sqlite-wasm@9.4.0
- @objectstack/plugin-auth@9.4.0
- @objectstack/plugin-org-scoping@9.4.0
- @objectstack/plugin-security@9.4.0
- @objectstack/service-cluster@9.4.0
- @objectstack/service-i18n@9.4.0
- @objectstack/types@9.4.0
-
1ada658: ADR-0046 P1: package documentation as metadata. New
docmetadata element — flat Markdown files undersrc/docs/*.mdcompile intodocs: DocSchema[]on the stack and register like any other metadata.- spec:
DocSchema({ name, label?, content }) insystem/,StackDefinition.docs,docinMetadataTypeSchema+ type registry (inert data, runtime-creatable) + canonical schema map,docs → docplural mapping. - cli:
os buildcollects flatsrc/docs/*.md(frontmattertitle:/first#heading → label) and enforces the ADR lint — flat directory, namespace-prefixed snake_case names, namespace required when docs ship, MDX/image ban, same-package relative-link resolution. Same rules surface inos lint. - objectql:
docsjoins the generic metadata registration loop (manifest + nested plugins). - runtime: docs count as app payload;
GET /metadata/doclist responses omitcontentby default (?include=contentopts in) so unbounded manuals stay off hot paths.
- spec:
-
Updated dependencies [1ada658]
-
Updated dependencies [b08d08d]
-
Updated dependencies [6259882]
-
Updated dependencies [3219191]
-
Updated dependencies [290f631]
-
Updated dependencies [50b7b47]
-
Updated dependencies [f15d6f6]
-
Updated dependencies [f8684ea]
-
Updated dependencies [b4765be]
-
Updated dependencies [b10aa78]
-
Updated dependencies [2796a1f]
- @objectstack/spec@9.3.0
- @objectstack/objectql@9.3.0
- @objectstack/rest@9.3.0
- @objectstack/metadata@9.3.0
- @objectstack/core@9.3.0
- @objectstack/formula@9.3.0
- @objectstack/observability@9.3.0
- @objectstack/driver-memory@9.3.0
- @objectstack/driver-sql@9.3.0
- @objectstack/driver-sqlite-wasm@9.3.0
- @objectstack/plugin-auth@9.3.0
- @objectstack/plugin-org-scoping@9.3.0
- @objectstack/plugin-security@9.3.0
- @objectstack/service-cluster@9.3.0
- @objectstack/service-i18n@9.3.0
- @objectstack/types@9.3.0
- Updated dependencies [2f57b75]
- Updated dependencies [2f57b75]
- @objectstack/spec@9.2.0
- @objectstack/core@9.2.0
- @objectstack/formula@9.2.0
- @objectstack/metadata@9.2.0
- @objectstack/objectql@9.2.0
- @objectstack/observability@9.2.0
- @objectstack/driver-memory@9.2.0
- @objectstack/driver-sql@9.2.0
- @objectstack/driver-sqlite-wasm@9.2.0
- @objectstack/plugin-auth@9.2.0
- @objectstack/plugin-org-scoping@9.2.0
- @objectstack/plugin-security@9.2.0
- @objectstack/rest@9.2.0
- @objectstack/service-cluster@9.2.0
- @objectstack/service-i18n@9.2.0
- @objectstack/types@9.2.0
- Updated dependencies [b9062c9]
- @objectstack/spec@9.1.0
- @objectstack/core@9.1.0
- @objectstack/formula@9.1.0
- @objectstack/metadata@9.1.0
- @objectstack/objectql@9.1.0
- @objectstack/observability@9.1.0
- @objectstack/driver-memory@9.1.0
- @objectstack/driver-sql@9.1.0
- @objectstack/driver-sqlite-wasm@9.1.0
- @objectstack/plugin-auth@9.1.0
- @objectstack/plugin-org-scoping@9.1.0
- @objectstack/plugin-security@9.1.0
- @objectstack/rest@9.1.0
- @objectstack/service-cluster@9.1.0
- @objectstack/service-i18n@9.1.0
- @objectstack/types@9.1.0
- Updated dependencies [1817845]
- @objectstack/spec@9.0.1
- @objectstack/core@9.0.1
- @objectstack/formula@9.0.1
- @objectstack/metadata@9.0.1
- @objectstack/objectql@9.0.1
- @objectstack/observability@9.0.1
- @objectstack/driver-memory@9.0.1
- @objectstack/driver-sql@9.0.1
- @objectstack/driver-sqlite-wasm@9.0.1
- @objectstack/plugin-auth@9.0.1
- @objectstack/plugin-org-scoping@9.0.1
- @objectstack/plugin-security@9.0.1
- @objectstack/rest@9.0.1
- @objectstack/service-cluster@9.0.1
- @objectstack/service-i18n@9.0.1
- @objectstack/types@9.0.1
- Updated dependencies [4c3f693]
- Updated dependencies [0bf39f1]
- Updated dependencies [f533f42]
- Updated dependencies [1c83ee8]
- @objectstack/spec@9.0.0
- @objectstack/plugin-auth@9.0.0
- @objectstack/core@9.0.0
- @objectstack/formula@9.0.0
- @objectstack/metadata@9.0.0
- @objectstack/objectql@9.0.0
- @objectstack/observability@9.0.0
- @objectstack/driver-memory@9.0.0
- @objectstack/driver-sql@9.0.0
- @objectstack/driver-sqlite-wasm@9.0.0
- @objectstack/plugin-org-scoping@9.0.0
- @objectstack/plugin-security@9.0.0
- @objectstack/rest@9.0.0
- @objectstack/service-cluster@9.0.0
- @objectstack/service-i18n@9.0.0
- @objectstack/types@9.0.0
- @objectstack/spec@8.0.1
- @objectstack/core@8.0.1
- @objectstack/types@8.0.1
- @objectstack/metadata@8.0.1
- @objectstack/objectql@8.0.1
- @objectstack/observability@8.0.1
- @objectstack/formula@8.0.1
- @objectstack/rest@8.0.1
- @objectstack/driver-memory@8.0.1
- @objectstack/driver-sql@8.0.1
- @objectstack/driver-sqlite-wasm@8.0.1
- @objectstack/plugin-auth@8.0.1
- @objectstack/plugin-org-scoping@8.0.1
- @objectstack/plugin-security@8.0.1
- @objectstack/service-cluster@8.0.1
- @objectstack/service-i18n@8.0.1
-
f68be58: feat(runtime): API-key generation endpoint — show-once
sys_api_key(ADR-0036, closes framework#1629)Adds
POST /api/v1/keys— the only path that mints asys_api_key. Phase 1a shipped key verification and thegenerateApiKey()primitive; this is the missing generation half that unblocks the self-serve connect flow.- Requires an authenticated principal; returns the raw secret exactly once
(
{ id, name, prefix, key }). Only the sha256 hash is persisted — the raw key is never stored, logged, or re-displayable. - Security (zero-tolerance):
user_idis pinned to the caller and never read from the body (no impersonation); the body is whitelisted toname(+ optional validated futureexpires_at) — anykey/id/user_id/revokedin the body is ignored, so a caller cannot forge a known-secret or escalate. The row is written with an elevated{ isSystem: true }context (sys_api_key is protection-locked) with server-controlled contents. Anonymous → 401; non-POST → 405; past/unparseableexpires_at→ 400. scopesare intentionally NOT accepted from the body in v1 (the verify path adds scopes to permissions, so honouring arbitrary body scopes would be an escalation vector); a generated key acts exactly AS the caller viauser_idresolution. Scoped/narrowing keys need subset-enforcement — deferred.
11 security tests (show-once, hash-not-raw persisted, round-trip auth via the verify path, impersonation blocked, forgery blocked, 401/405/400, expiry end-to-end). Full runtime suite green (376).
- Requires an authenticated principal; returns the raw secret exactly once
(
-
bc0d85b: feat(mcp): Streamable HTTP transport — every app is a network-reachable MCP server (ADR-0036 Phase 2)
The MCP server plugin spoke stdio only, so a remote agent (Claude Desktop / Cursor) could not connect to a hosted env. This adds the Streamable HTTP transport and wires it into the runtime's request path, building on the Phase 1a
sys_api_keyauth foundation.-
@objectstack/mcp(renamed from@objectstack/plugin-mcp-server— see the rename changeset)MCPServerRuntime.handleHttpRequest(request, { bridge, parsedBody })— serves one MCP request over the Web-standardWebStandardStreamableHTTPServerTransport(runs on Node 18+, Workers, Deno, Bun). Stateless: a fresh, isolatedMcpServer+ transport is built per request (the SDK-recommended pattern), in JSON-response mode so the response is fully buffered — no streaming pass-through concerns over the Worker→container hop.- New
registerObjectTools+McpDataBridge(mcp-http-tools.ts): the object-CRUD tool set (list_objects,describe_object,query_records,get_record,create_record,update_record,delete_record). All execution is delegated to an injected, principal-bound bridge — the tool layer never touches the data engine directly. System (sys_*) objects are not exposed by default (fail-closed guard on every object-scoped tool). The internal AI/authoring toolRegistry is deliberately NOT bridged onto the external surface.
-
@objectstack/runtimeHttpDispatcherserves/mcp: opt-in viaOS_MCP_SERVER_ENABLED=true(404 when off, so the surface isn't advertised); fail-closed auth (anonymous → 401 — requires the principal resolved by Phase 1a's API-key path or a session). It builds anMcpDataBridgethat runs every operation through the existingcallDatapath bound to the request'sExecutionContext, so external agents run under the key's permissions + RLS, never a parallel or escalated path. The discovery endpoint advertisesmcponly when enabled.
Security: every external MCP entry runs as the scoped
sys_api_keyprincipal under existing object permissions + RLS; MCP is opt-in per env; no raw keys or secrets cross the wire. Fully unit-tested (transport handshake/tools, gate, auth, principal binding). -
-
2537e28: fix(runtime): adapt node/Hono req → Web Request for the MCP transport (ADR-0036)
The MCP Streamable HTTP transport needs a Web-standard
Request, but the runtime HTTP adapter hands the dispatcher a node/Hono-style req (plainheadersobject, path-onlyurl).handleMcprejected it with 400 ("MCP transport requires a standard HTTP request") — so the live endpoint was unusable even once routed + registered. Unit tests passed a realRequest, hiding it; caught in staging e2e oninitialize.handleMcpnow reconstructs a WebRequest(method, absolute URL from host+path, normalised headers, JSON body from the parsed body) when the inbound req isn't already Web-standard. Regression tests cover a POST and a GET node-style req. -
0ec7717: fix(runtime): mount /mcp and /keys HTTP routes (ADR-0036) — were unreachable
The dispatcher mounts routes EXPLICITLY on the HTTP server (no catch-all). The MCP transport (#1626) and key-generation (#1630) added branches inside
dispatch()but never registered the correspondingserver.<verb>()routes, so/api/v1/mcpand/api/v1/keys404'd at the HTTP layer before ever reaching the dispatcher. Unit tests called the handlers directly, hiding the gap; it only showed up in live staging e2e.- Register
/mcp(GET/POST/DELETE → dispatch, transport reads the method) and/keys(POST) in the dispatcher plugin, routed throughdispatch()so the host's project-aware kernel swap + executionContext resolution run first. - Add
dispatcher-plugin.routes.test.tsasserting the routes are registered (the regression that would have caught this).
- Register
-
c262301: fix(rest): REST data API honors sys_api_key — one shared verifier with MCP (closes #1633)
Staging e2e found the MCP surface authenticated a
sys_api_keybut the REST data API (@objectstack/rest) returned 401 for the same key — itsresolveExecCtxonly checked the better-auth session, never the API key.Converged both surfaces onto ONE verifier so they can't drift:
@objectstack/core/securitynow owns the sharedsys_api_keyprimitives (hashApiKey,generateApiKey,extractApiKey,parseScopes,isExpired) plus a newresolveApiKeyPrincipal(ql, headers, nowMs?)that hashes the inbound key, looks it up by the indexed at-rest hash, and rejects unknown / revoked / expired / owner-less keys (fail-closed).coreis the natural home: bothrestandruntimedepend on it, it depends on neither (no cycle), and it's server-side (already usesnode:crypto).@objectstack/runtime—security/api-key.tsre-exports the primitives from core (stable import surface) andresolveExecutionContextnow delegates its API-key branch toresolveApiKeyPrincipal.@objectstack/rest—resolveExecCtxresolves the data engine once and triesresolveApiKeyPrincipal(x-api-key /Authorization: ApiKey) BEFORE the session, so/api/v1/data+/api/v1/metanow authenticate an API key under the key's permissions + RLS, exactly like the dispatcher/MCP path.
Tests: core
api-key.test.ts(primitives + verifier: valid / revoked / expired / unknown / owner-less / plaintext-not-matched / fail-closed-ql). runtime + rest suites green. -
Updated dependencies [a46c017]
-
Updated dependencies [b990b89]
-
Updated dependencies [99111ec]
-
Updated dependencies [d5a8161]
-
Updated dependencies [5cf1f1b]
-
Updated dependencies [9ef89d4]
-
Updated dependencies [e6374b5]
-
Updated dependencies [1e8b680]
-
Updated dependencies [0a6438e]
-
Updated dependencies [3306d2f]
-
Updated dependencies [ae7fb3f]
-
Updated dependencies [c262301]
-
Updated dependencies [e1478fe]
-
Updated dependencies [bc44195]
-
Updated dependencies [9e2e229]
-
Updated dependencies [345e189]
- @objectstack/spec@8.0.0
- @objectstack/objectql@8.0.0
- @objectstack/driver-sql@8.0.0
- @objectstack/plugin-auth@8.0.0
- @objectstack/plugin-security@8.0.0
- @objectstack/rest@8.0.0
- @objectstack/core@8.0.0
- @objectstack/formula@8.0.0
- @objectstack/metadata@8.0.0
- @objectstack/observability@8.0.0
- @objectstack/driver-memory@8.0.0
- @objectstack/driver-sqlite-wasm@8.0.0
- @objectstack/plugin-org-scoping@8.0.0
- @objectstack/service-cluster@8.0.0
- @objectstack/service-i18n@8.0.0
- @objectstack/types@8.0.0
-
ac1fc4c: feat(metadata): draft-overlay reads so an admin can render the console off pending drafts before publish
ADR-0033's loop is
build (draft) → review → publish, but "review" was only a JSON diff — the one thing that actually confirms an AI/hand-authored change (the rendered object page / kanban / form / nav) only existed after publish. That forces publishing unreviewed metadata just to look at it, defeating the draft gate.This adds a request-scoped draft-overlay read mode to the metadata resolution layer:
getMetaItems({ …, previewDrafts })— after the active overlay, overlaysstate='draft'rows on top (draft WINS on name collision; draft-only items surface too). Drafts are never hydrated into the process-wide SchemaRegistry.getMetaItem({ …, previewDrafts })— non-strict: prefers a draft row if one exists, else falls back to the active value (unlike the strictstate:'draft'mode, which 404sno_draft).- Every overlaid item is tagged
_draft: trueso the UI can badge it and show a "preview" banner. - The runtime HTTP dispatcher threads
?preview=draftonGET /metadata/:typeandGET /metadata/:type/:nameinto these reads.
The same overlay also unblocks the AI authoring agent referencing its own just-drafted objects (a follow-up will point
list_metadataat it). Admin gating of the?preview=draftflag is a deliberate follow-up step.Note: a brand-new draft object has no physical table until publish, so preview renders its shape (form/view/kanban/nav) but shows no data; field-additions to existing objects preview fully.
-
ac1fc4c: feat(packages): one-click discard-drafts and full delete for a package
Two distinct package-level lifecycle operations, both built on the per-item delete primitive:
-
discardPackageDrafts(packageId)— drop every pending DRAFT bound to the package, reverting it to its last published baseline. NON-destructive: active/published metadata and physical tables are untouched. Use case: "I edited this app for a while and it turned out worse than before — abandon all my changes." Routes through the sys_metadata path (no metadata-service dependency, unlike the existingPOST /packages/:id/revert, which 503s without a metadata service). REST:POST /packages/:id/discard-drafts. -
deletePackage(packageId)— remove the ENTIRE package: everysys_metadatarow (active + draft) and, by default, the physical table of each object it defined (DESTRUCTIVE).keepData: trueremoves metadata but preserves tables; thesys_-table guard still applies. Use case: "I don't want this package anymore."DELETE /packages/:idnow performs this persisted removal in addition to the in-memory registry unregister it already did (previously it left AI/runtime packages' rows and tables behind);?keepData=trueopts out of teardown.
Drafts are deleted before active rows so each object's table is torn down exactly once. Per-item failures are collected without aborting the rest.
-
-
Updated dependencies [ac1fc4c]
-
Updated dependencies [ac1fc4c]
-
Updated dependencies [ac1fc4c]
- @objectstack/objectql@7.9.0
- @objectstack/rest@7.9.0
- @objectstack/spec@7.9.0
- @objectstack/core@7.9.0
- @objectstack/types@7.9.0
- @objectstack/metadata@7.9.0
- @objectstack/observability@7.9.0
- @objectstack/formula@7.9.0
- @objectstack/driver-memory@7.9.0
- @objectstack/driver-sql@7.9.0
- @objectstack/driver-sqlite-wasm@7.9.0
- @objectstack/plugin-auth@7.9.0
- @objectstack/plugin-org-scoping@7.9.0
- @objectstack/plugin-security@7.9.0
- @objectstack/service-cluster@7.9.0
- @objectstack/service-i18n@7.9.0
-
a75823a: feat(metadata): expose pending DRAFT metadata (ADR-0033 draft discoverability)
AI-authored metadata lands as drafts (
sys_metadatarows withstate='draft', bound to an app package), but the only list path —getMetaItems— reads the active registry, so drafts were invisible: a just-built app package looked empty and there was no "pending changes" surface.SysMetadataRepository.listDrafts({type?, packageId?})lists draft rows (mirrorslist()but scoped tostate='draft', optionally narrowed by package), returning a light header projection (no body) withpackageId.protocol.listDrafts({packageId?, type?, organizationId?})exposes it over the overlay repo.GET /api/v1/meta/_drafts?packageId=&type=surfaces it to the console. Registered in the REST server before the greedy/meta/:typeroute (and mirrored in the dispatcher) so_draftsis never captured as a metadata type name.
Read-only; no behavior change to existing list/publish paths. Powers the upcoming Studio "drafts/pending changes" view and draft-aware package contents.
-
4fbb86a: feat(packages): consolidate the package subsystem so AI-built app packages surface in Studio
The package subsystem was split across two stores that never met: the in-memory
SchemaRegistry(what the dispatcher's/api/v1/packageslist/detail andgetMetaItems({type:'package'})read — i.e. Studio's package selector) and the durablesys_packagestable (where the AI's auto app package, and anypackage-service publish, were written). Nothing reconciled the two, so an AI-createdapp.<name>package never appeared in Studio.This unifies them around one write primitive and one read source:
protocol.installPackageis now implemented (it was declared-but-missing). It is the single canonical write path: it registers the package in the in-memory registry and best-effort persists it tosys_packagesvia thepackageservice. Non-fatal when nopackageservice is wired (registry write still succeeds).- Dispatcher
POST /api/v1/packagesroutes throughprotocol.installPackage(falling back to the bare registry write when the protocol is unavailable), so HTTP installs are durable too. @objectstack/service-packagereconcilessys_packagesback into the registry on boot, without clobbering filesystem-registered packages — so persisted packages survive a restart and stay visible in the registry-backed read paths.@objectstack/service-aiapply_blueprintnow homes an app viaprotocol.installPackage(falling back to the legacypackage-service publish), so the app package lands where Studio reads it.
Still the legacy
package_idplane — sealedsys_package_versionversioning and cross-environment promotion remain ADR-0027 follow-ups. -
e631f1e: feat(metadata): publish a whole app's drafts in one shot (ADR-0033)
After an AI builds an app, its metadata is drafted (bound to an app package) and had to be published one item at a time. The package-level
POST /packages/:id/publishneeds themetadataservice (503 when absent, e.g. the showcase) and reads the in-memory registry, not the drafts.protocol.publishPackageDrafts({ packageId })promotes everysys_metadatadraft row bound to the package to active by reusing the per-itempublishMetaItemprimitive (overridable/lock guards + runtime registry refresh). Per-item failures are collected, not fatal. Nometadata-service dependency.POST /api/v1/packages/:id/publish-draftsexposes it (distinct from the registry-based/publish), returning{ success, publishedCount, failedCount, published, failed }.
Verified live: an AI-built
app.asset_management(4 drafts) published in one call — all 4 promoted to active, drafts cleared, draft objects became queryable. -
424ab26: fix(seed): reject object-wrapped relationship references and constrain them at compile time
Seed datasets resolve
lookup/master_detailreferences by matching the value against the target record's externalId — so the value must be the plain natural-key string (e.g.account: 'Acme Corp'), never a wrapper object likeaccount: { externalId: 'Acme Corp' }. The wrapper was silently skipped by the loader, fell through unresolved, and reached the SQL driver as a non-bindable value — masked on an always-empty:memory:DB but crashing on a persistent one with "SQLite3 can only bind numbers, strings, bigints, buffers, and null" once seeds re-ran as updates.defineDatasetnow constrains reference fields tostring | nullat compile time (derived from each field'stype), so the object form is a type error.SeedLoaderServicenow fails loudly with an actionable message (and drops the value instead of handing it to the driver) when a reference is an object — consistent behavior across all drivers, no longer silently masked.
-
Updated dependencies [06f2bbb]
-
Updated dependencies [a75823a]
-
Updated dependencies [4fbb86a]
-
Updated dependencies [e631f1e]
-
Updated dependencies [f01f9fa]
-
Updated dependencies [6fc2678]
-
Updated dependencies [36719db]
-
Updated dependencies [424ab26]
- @objectstack/spec@7.8.0
- @objectstack/objectql@7.8.0
- @objectstack/rest@7.8.0
- @objectstack/formula@7.8.0
- @objectstack/core@7.8.0
- @objectstack/metadata@7.8.0
- @objectstack/observability@7.8.0
- @objectstack/driver-memory@7.8.0
- @objectstack/driver-sql@7.8.0
- @objectstack/driver-sqlite-wasm@7.8.0
- @objectstack/plugin-auth@7.8.0
- @objectstack/plugin-org-scoping@7.8.0
- @objectstack/plugin-security@7.8.0
- @objectstack/service-cluster@7.8.0
- @objectstack/service-i18n@7.8.0
- @objectstack/types@7.8.0
- Updated dependencies [b391955]
- Updated dependencies [f06b64e]
- Updated dependencies [825ab06]
- Updated dependencies [023bf93]
- Updated dependencies [764c747]
- @objectstack/spec@7.7.0
- @objectstack/formula@7.7.0
- @objectstack/metadata@7.7.0
- @objectstack/objectql@7.7.0
- @objectstack/driver-sql@7.7.0
- @objectstack/core@7.7.0
- @objectstack/observability@7.7.0
- @objectstack/driver-memory@7.7.0
- @objectstack/driver-sqlite-wasm@7.7.0
- @objectstack/plugin-auth@7.7.0
- @objectstack/plugin-org-scoping@7.7.0
- @objectstack/plugin-security@7.7.0
- @objectstack/rest@7.7.0
- @objectstack/service-cluster@7.7.0
- @objectstack/service-i18n@7.7.0
- @objectstack/types@7.7.0
-
8e539cc: Implement the
/api/v1/notificationsREST surface (ADR-0030)The notification REST routes (
GET /notifications,POST /notifications/read,POST /notifications/read/all) were declared in the spec but never had a server-side handler — no plugin registered thenotificationcore service, so the routes were never advertised in discovery andclient.notifications.*calls 404'd. (The Console bell works today only because it bypasses these endpoints and reads the inbox via the generic data API.)This wires the surface end-to-end against the ADR-0030 L5 model:
MessagingServicegains an inbox read API:listInbox(userId, opts)readssys_inbox_messagejoined withsys_notification_receiptfor read-state (a message is unread until its event has aread/clicked/dismissedreceipt);markRead(userId, ids)andmarkAllRead(userId)upsert the receipt toread, keyed(notification_id, user_id, channel:'inbox')— updating the existingdeliveredreceipt in place, inserting only when absent. No reliance on the re-modeledsys_notificationL2 event (which carries no recipient/read columns).MessagingServicePluginnow also registers the messaging service under thenotificationcore service slot, so the dispatcher resolves + advertises the routes. The legacyINotificationService.send()abstraction is unused and unconsumed.HttpDispatchergainshandleNotification+ a/notificationsdispatch branch: it takes the authenticated user from the execution context and maps list / mark-read / mark-all-read to the service. Responses match the spec schemas ({ notifications, unreadCount },{ success, readCount }).
Pairs with the objectui SDK consumer repoint (
useClientNotifications→markRead/registerDevicesignatures). Device registration and preference endpoints remain out of scope (unimplemented as before).
- Updated dependencies [955d4c8]
- Updated dependencies [c4a4cbd]
- Updated dependencies [b046ec2]
- Updated dependencies [2170ad9]
- Updated dependencies [02d6359]
- Updated dependencies [7648242]
- Updated dependencies [8c01eea]
- Updated dependencies [8fa1e7f]
- Updated dependencies [be20aa4]
- Updated dependencies [55866f5]
- Updated dependencies [b7a4f14]
- Updated dependencies [60f9c45]
- @objectstack/spec@7.6.0
- @objectstack/formula@7.6.0
- @objectstack/objectql@7.6.0
- @objectstack/plugin-auth@7.6.0
- @objectstack/driver-sqlite-wasm@7.6.0
- @objectstack/core@7.6.0
- @objectstack/metadata@7.6.0
- @objectstack/observability@7.6.0
- @objectstack/driver-memory@7.6.0
- @objectstack/driver-sql@7.6.0
- @objectstack/plugin-org-scoping@7.6.0
- @objectstack/plugin-security@7.6.0
- @objectstack/rest@7.6.0
- @objectstack/service-cluster@7.6.0
- @objectstack/service-i18n@7.6.0
- @objectstack/types@7.6.0
- @objectstack/spec@7.5.0
- @objectstack/core@7.5.0
- @objectstack/types@7.5.0
- @objectstack/metadata@7.5.0
- @objectstack/objectql@7.5.0
- @objectstack/observability@7.5.0
- @objectstack/formula@7.5.0
- @objectstack/rest@7.5.0
- @objectstack/driver-memory@7.5.0
- @objectstack/driver-sql@7.5.0
- @objectstack/driver-sqlite-wasm@7.5.0
- @objectstack/plugin-auth@7.5.0
- @objectstack/plugin-org-scoping@7.5.0
- @objectstack/plugin-security@7.5.0
- @objectstack/service-cluster@7.5.0
- @objectstack/service-i18n@7.5.0
- @objectstack/spec@7.4.1
- @objectstack/core@7.4.1
- @objectstack/types@7.4.1
- @objectstack/metadata@7.4.1
- @objectstack/objectql@7.4.1
- @objectstack/observability@7.4.1
- @objectstack/formula@7.4.1
- @objectstack/rest@7.4.1
- @objectstack/driver-memory@7.4.1
- @objectstack/driver-sql@7.4.1
- @objectstack/driver-sqlite-wasm@7.4.1
- @objectstack/plugin-auth@7.4.1
- @objectstack/plugin-org-scoping@7.4.1
- @objectstack/plugin-security@7.4.1
- @objectstack/service-cluster@7.4.1
- @objectstack/service-i18n@7.4.1
-
2faf9f2: External Datasource Federation (ADR-0015) — boot-validation gate (Gate 2).
Adds
ExternalValidationPlugin(createExternalValidationPlugin) which, onkernel:ready, validates every federated object against its remote table via theexternal-datasourceservice and applies the datasource'sexternal.validation.onMismatchpolicy:fail(throwsExternalSchemaMismatchError, aborting boot — the default),warn(logs the diff), orignore. No-op when federation is unused. -
394d34f: Messaging + triggers capability tokens, and notify-by-email recipient resolution.
Make the
notifyflow node and auto-firing flows usable from a plaindefineStack({ requires: [...] })— no hand-wired plugin instances.- CLI / runtime — new capability tokens.
messaging→MessagingServicePlugin(thenotifynode delivers to the inbox channel instead of degrading to a logged no-op);triggers→RecordChangeTriggerPlugin+ScheduleTriggerPlugin(autolaunched / schedule flows actually fire — pairtriggerswithjobfor cron/interval). Wired identically in the CLICAPABILITY_PROVIDERStable and the runtimecapability-loader. - Inbox channel — notify-by-email. Flows commonly address recipients by
email (e.g.
{record.assignee}), butsys_inbox_messageis keyed by user id. The inbox channel now resolves an email-shaped recipient to itssys_user.id(configurable viaInboxChannelOptions.userObject), with a verbatim fallback when the recipient is not email-shaped, no user matches, or the lookup fails — so a failed resolution can never drop the row.
- CLI / runtime — new capability tokens.
-
ff3d006: Screen-flow runtime — interactive
screennodes (suspend → render → resume).A
screennode that declares input fields now suspends the run on entry (reusing the ADR-0019 durable pause), surfaces aScreenSpecdescribing the form, and resumes with the collected values applied as bare flow variables so downstream nodes read them via{var}. (waitForInput: falseforces the old server pass-through.)- spec:
AutomationResult.screen?: ScreenSpec,ResumeSignal.variables?(bare vars),IAutomationService.getSuspendedScreen?(runId). - service-automation: the
screenexecutor builds theScreenSpecand suspends when fields are present; the suspend/resume plumbing threads the screen throughFlowSuspendSignal→SuspendedRun→ the paused result;resume()setssignal.variablesas bare flow variables;getSuspendedScreen. - runtime:
POST /api/v1/automation/:name/runs/:runId/resume(body{ inputs }) andGET …/runs/:runId/screen, wired through both the dispatcher route table andhandleAutomation.
Verified end-to-end headlessly: the showcase Reassign Wizard launches → pauses at the "New Assignee" screen → resumes with the input → the task is reassigned. The objectui
FlowRunnerUI that renders these screens ships separately. - spec:
-
5e831de: Seed data: first-class identity binding + loud failures (fixes #1389)
Records seeded via
defineDataset/defineStack({ data })can now bind to a platform user withcel\os.user.id`(and to the org withcel`os.org.id``), which previously never resolved at boot.os.user/os.orgnow actually resolve. The runtime provisions a deterministic, non-loginable system user (usr_system, rolesystem) before any seed runs and binds it toos.user, so identity-derived seed values resolve even on a fresh boot — before the first human sign-up. The human login admin remains a separate better-auth identity and need not own seed data. Exposed as the canonicalSystemUserId.SYSTEMconstant.- New
SeedLoaderConfig.identitycarries theos.user/os.orgsubject into CEL evaluation (@objectstack/spec). - Failures are loud, not silent. A record whose CEL value can't resolve
(e.g. a required
cel\os.user.id`` with no identity) — or that fails to write — is now counted as an error, marks the load unsuccessful, and logs an actionable message, instead of being silently dropped.
-
23c7107: ADR-0020 — converge the three "state machine" declaration shapes to one enforced
state_machinevalidation rule.Before this change a record state machine could be declared three ways (a
workflowmetadata type, anobject.stateMachinesmap, or astate_machinevalidation rule) and none of them were enforced at runtime — a declarative guardrail that was pure decoration, and a hallucination trap for AI authors.Enforcement (
@objectstack/objectql)- New
validation/rule-validator.tsevaluates the object'svalidationsunion on the write path:evaluateValidationRules,needsPriorRecord, and thelegalNextStatesintrospection helper (all exported from the package root). state_machinerules reject illegalfieldtransitions on update (with the rule'smessage);script/cross_fieldpredicate rules now also fire (they were silently broken on PATCH updates because only the patch, not the prior record, was available). The engine plumbs the prior record into rule evaluation on single-row update; multi-row (updateMany) updates log a warning and skip rule evaluation rather than enforce on incomplete data.
Convergence / retirement (
@objectstack/spec) — breaking- Retires the
workflowmetadata type (removed from the metadata-type enum, the registry, the schema map, theworkflowscollection key, and the plural→singular mapping). - Removes the
object.stateMachinesmap and thestack.workflowsarray. Thestate_machinevalidation rule is the single canonical home. - The XState-style
StateMachineSchemafile is kept (still used by the agent conversation lifecycle and the discovery protocol); only its role as theworkflowmetadata-type backing schema was removed. The optionalworkflowRPC service surface (CoreServiceName.workflow,/api/v1/workflow,IWorkflowService) is kept as a documented follow-up.
Introspection (
@objectstack/runtime)- Adds
GET /metadata/objects/:name/state/:field?from=:state, returning the legal next states for a field (next: nullwhen no FSM governs the field,[]for a declared dead-end) so UIs/agents read the transition table instead of re-deriving it.
Surfaces (
@objectstack/platform-objects,@objectstack/cli)- Studio drops the standalone "Workflow Rules" nav (state machines are edited alongside the object's other validation rules).
explainno longer listsworkflowas a related metadata type.
Migration: replace a
workflow/StateMachineConfigdeclaration with astate_machinevalidation rule on the object (field+{ from: [allowedTo] }transition table), and move any side-effecting actions (emails, task creation) into a record-triggered or scheduled Flow (ADR-0019). See the migratedexamples/app-crmflows for the pattern. - New
-
13632b1: ADR-0030 P0 (framework) — converge notifications onto a single ingress and the layered model. Every producer now publishes through
NotificationService.emit(EmitInput); the in-app inbox is a materialization of delivery, not a row producers write.Single ingress (
@objectstack/service-messaging) — breakingMessagingService.emittakes the newEmitInputcontract (topic/audience/payload/severity/dedupKey/source/actorId/organizationId/channels) instead of the flatNotificationshape. It writes the L2sys_notificationevent (idempotent ondedupKey), resolves the audience, then fans out; it returns{ notificationId, deduped, deliveries, delivered, failed }.- New
sys_notification_receiptobject — the read-state spine (delivered|read|clicked|dismissed), keyed(notification_id, user_id, channel). The inbox channel writes adeliveredreceipt on materialization. sys_inbox_message: addsnotification_id/delivery_id, dropsread(read-state moved to the receipt), adds the userminelist view.
Event re-model (
@objectstack/platform-objects) — breakingsys_notificationis re-modeled from a per-user inbox into the L2 event (topic,payload,severity,dedup_key,source_*,actor_id). Removesrecipient_id/is_read/read_at/type/title/body/url/actor_nameand the inbox actions/views. App-nav: the account inbox points atsys_inbox_message; Setup shows the notification event log.
Producers routed through
emit()@objectstack/service-automation: thenotifynode maps its config toEmitInput.@objectstack/plugin-audit: collaboration@mention→collab.mentionand assignment →collab.assignment(both with adedupKey); no more directsys_notificationwrites. Collaboration notifications now requireMessagingServicePlugin(they degrade to a warn otherwise).
Migration (
@objectstack/metadata)- Idempotent
migrateSysNotificationToEventsplits legacysys_notificationinbox rows intosys_inbox_message+ receipts and rewrites the event row.
Startup (
@objectstack/cli,@objectstack/runtime)messagingis now a foundational capability. Onobjectstack serveit is added toALWAYS_ON_CAPABILITIES(every non-minimalpreset starts it); on cloud per-project kernels the capability loader expandsrequiresto addmessagingwheneverauditis present. This keeps collaboration@mention/ assignment notifications (which now flow through the pipeline) working out of the box on both paths.--preset minimalopts out.
The Console bell repoint (objectui) and phases P1–P3 are tracked in
docs/handoff/adr-0030-notification-convergence.md. -
Updated dependencies [23c7107]
-
Updated dependencies [c72daad]
-
Updated dependencies [4404572]
-
Updated dependencies [eea3f1b]
-
Updated dependencies [e478e0c]
-
Updated dependencies [13632b1]
-
Updated dependencies [f115182]
-
Updated dependencies [24c9013]
-
Updated dependencies [2faf9f2]
-
Updated dependencies [2faf9f2]
-
Updated dependencies [2faf9f2]
-
Updated dependencies [2faf9f2]
-
Updated dependencies [2faf9f2]
-
Updated dependencies [a6d4cbb]
-
Updated dependencies [08fbbb4]
-
Updated dependencies [58b450b]
-
Updated dependencies [82eb6cf]
-
Updated dependencies [13d8653]
-
Updated dependencies [ff3d006]
-
Updated dependencies [5e831de]
- @objectstack/spec@7.4.0
- @objectstack/objectql@7.4.0
- @objectstack/plugin-auth@7.4.0
- @objectstack/plugin-security@7.4.0
- @objectstack/metadata@7.4.0
- @objectstack/driver-sql@7.4.0
- @objectstack/rest@7.4.0
- @objectstack/core@7.4.0
- @objectstack/formula@7.4.0
- @objectstack/observability@7.4.0
- @objectstack/driver-memory@7.4.0
- @objectstack/driver-sqlite-wasm@7.4.0
- @objectstack/plugin-org-scoping@7.4.0
- @objectstack/service-cluster@7.4.0
- @objectstack/service-i18n@7.4.0
- @objectstack/types@7.4.0
- Updated dependencies [5e7c554]
- @objectstack/spec@7.3.0
- @objectstack/core@7.3.0
- @objectstack/formula@7.3.0
- @objectstack/metadata@7.3.0
- @objectstack/objectql@7.3.0
- @objectstack/observability@7.3.0
- @objectstack/driver-memory@7.3.0
- @objectstack/driver-sql@7.3.0
- @objectstack/driver-sqlite-wasm@7.3.0
- @objectstack/plugin-auth@7.3.0
- @objectstack/plugin-org-scoping@7.3.0
- @objectstack/plugin-security@7.3.0
- @objectstack/rest@7.3.0
- @objectstack/service-cluster@7.3.0
- @objectstack/service-i18n@7.3.0
- @objectstack/types@7.3.0
-
9096dfe:
OS_env-var prefix migration (issue #1382).All ObjectStack-owned environment variables now use the
OS_prefix. Legacy names still work for one release and emit a one-shot deprecation warning via the newreadEnvWithDeprecation()helper in@objectstack/types.Renamed (with legacy fallback):
New Legacy (deprecated) OS_AUTH_SECRETAUTH_SECRET,BETTER_AUTH_SECRETOS_AUTH_URLAUTH_BASE_URL,BETTER_AUTH_URL,OS_AUTH_BASE_URLOS_PORTPORTOS_DATABASE_URLDATABASE_URLOS_ROOT_DOMAINROOT_DOMAINOS_MULTI_ORG_ENABLEDOS_MULTI_TENANTOS_CORS_ENABLEDCORS_ENABLEDOS_CORS_ORIGINCORS_ORIGINOS_CORS_CREDENTIALSCORS_CREDENTIALSOS_CORS_MAX_AGECORS_MAX_AGEOS_AI_MODELAI_MODELOS_MCP_SERVER_ENABLEDMCP_SERVER_ENABLEDOS_MCP_SERVER_NAMEMCP_SERVER_NAMEOS_MCP_SERVER_TRANSPORTMCP_SERVER_TRANSPORTOS_NODE_IDOBJECTSTACK_NODE_IDOS_METADATA_WRITABLEOBJECTSTACK_METADATA_WRITABLEOS_DEV_CRYPTO_KEYOBJECTSTACK_DEV_CRYPTO_KEYOS_HOMEOBJECTSTACK_HOMEMigration: rename in your
.env. Legacy names continue to work this release and will be removed in a future major. Industry-standard names (NODE_ENV,HOME,OPENAI_API_KEY,TURSO_*, OAuth*_CLIENT_ID/SECRET,RESEND_API_KEY,POSTMARK_TOKEN,AI_GATEWAY_*,SMTP_*) are NOT renamed. -
Updated dependencies [9096dfe]
- @objectstack/types@7.2.1
- @objectstack/objectql@7.2.1
- @objectstack/plugin-auth@7.2.1
- @objectstack/metadata@7.2.1
- @objectstack/spec@7.2.1
- @objectstack/core@7.2.1
- @objectstack/observability@7.2.1
- @objectstack/formula@7.2.1
- @objectstack/rest@7.2.1
- @objectstack/driver-memory@7.2.1
- @objectstack/driver-sql@7.2.1
- @objectstack/driver-sqlite-wasm@7.2.1
- @objectstack/plugin-org-scoping@7.2.1
- @objectstack/plugin-security@7.2.1
- @objectstack/service-cluster@7.2.1
- @objectstack/service-i18n@7.2.1
- @objectstack/spec@7.2.0
- @objectstack/core@7.2.0
- @objectstack/types@7.2.0
- @objectstack/metadata@7.2.0
- @objectstack/objectql@7.2.0
- @objectstack/observability@7.2.0
- @objectstack/formula@7.2.0
- @objectstack/rest@7.2.0
- @objectstack/driver-memory@7.2.0
- @objectstack/driver-sql@7.2.0
- @objectstack/driver-sqlite-wasm@7.2.0
- @objectstack/plugin-auth@7.2.0
- @objectstack/plugin-org-scoping@7.2.0
- @objectstack/plugin-security@7.2.0
- @objectstack/service-cluster@7.2.0
- @objectstack/service-i18n@7.2.0
-
47a92f4: Promote
email_templateto a first-class metadata type using the canonicalEmailTemplateDefinitionSchema.Previously
email_templatehad two competing Zod schemas (Prime Directive #8 violation): the legacyEmailTemplateSchema(a sub-shape ofNotification) and the richerEmailTemplateDefinitionSchema. The runtime metadata protocol (packages/objectql/src/protocol.ts) and Studio's property panel registered the legacy one, which is why all the new fields (name,label,category,locale,bodyHtml,bodyText, …) were reported as “declared in form layout but missing from schema”.This change:
- Repoints the
email_templateentry inTYPE_TO_SCHEMA(packages/objectql/src/protocol.ts) and inBUILTIN_METADATA_TYPE_SCHEMAS(packages/spec/src/kernel/metadata-type-schemas.ts) toEmailTemplateDefinitionSchema. The legacyEmailTemplateSchemais kept only as an inline sub-shape insideNotification. - Adds an
emailTemplatescollection todefineStack()input (packages/spec/src/stack.zod.ts), registers it inMAP_SUPPORTED_FIELDS/PLURAL_TO_SINGULAR(packages/spec/src/shared/metadata-collection.zod.ts), wires it intoARTIFACT_FIELD_TO_TYPE(packages/metadata/src/plugin.ts) andAPP_CATEGORY_KEYS(packages/runtime/src/app-plugin.ts). - Rewrites
packages/spec/src/system/email-template.form.tsfor the new schema with sections for Identity, Subject, HTML body, Plain-text body, Variables, Delivery overrides, Status. - Ships three reference templates in
examples/app-crm/src/emails/:crm.deal_won(rewritten to canonical shape),crm.welcome(new),crm.lead_followup(new), and wires them into the CRM stack viaemailTemplates: Object.values(emails).
End-to-end verified in Studio: list view at
/_console/apps/studio/metadata/email_templateshows all three entries; the detail view renders the EmailTemplatePreview iframe and the property panel cleanly renders every canonical field (no missing-schema warnings).GET /api/v1/metanow returns the newpropertiesset (name, label, category, locale, subject, bodyHtml, bodyText, variables, fromOverride, replyTo, active, isSystem, description). - Repoints the
-
Updated dependencies [47a92f4]
- @objectstack/spec@7.1.0
- @objectstack/objectql@7.1.0
- @objectstack/metadata@7.1.0
- @objectstack/plugin-auth@7.1.0
- @objectstack/plugin-org-scoping@7.1.0
- @objectstack/plugin-security@7.1.0
- @objectstack/core@7.1.0
- @objectstack/formula@7.1.0
- @objectstack/observability@7.1.0
- @objectstack/driver-memory@7.1.0
- @objectstack/driver-sql@7.1.0
- @objectstack/driver-sqlite-wasm@7.1.0
- @objectstack/rest@7.1.0
- @objectstack/service-cluster@7.1.0
- @objectstack/service-i18n@7.1.0
- @objectstack/types@7.1.0
-
dc72172: Breaking: Removed
@objectstack/driver-tursoand@objectstack/knowledge-tursofrom the open-core framework.The Turso/libSQL driver and its native-vector knowledge adapter now ship exclusively with the ObjectStack Cloud distribution (
objectstack-ai/cloud). Rationale: Turso is used only for cloud/edge multi-tenant deployments — local development uses better-sqlite3 (faster), and the Turso integration is part of ObjectStack's commercial offering.@objectstack/driver-turso→objectstack-ai/cloud/packages/driver-turso@objectstack/knowledge-turso→objectstack-ai/cloud/packages/knowledge-tursoITursoPlatformServicecontract (spec/contracts/turso-platform.ts) — removed entirelyTursoConfigSchema,TursoDriverSpec,TursoMultiTenantConfigSchema,TenantResolverStrategySchema, etc. — moved into@objectstack/driver-turso(re-exported from cloud)
packages/runtime/src/standalone-stack.ts:databaseDriverenum no longer accepts'turso';libsql:///https://URL detection removed. Cloud builds register the Turso driver via their own stack composition.packages/runtime/src/cloud/artifact-environment-registry.ts: droppedcase 'libsql'/'turso'. Cloud has its ownArtifactEnvironmentRegistrythat handles Turso.packages/cli/src/commands/serve.ts: removeddriverType === 'turso' | 'libsql'branch.packages/runtime/package.json,packages/cli/package.json: removed optional peerDep on@objectstack/driver-turso.packages/runtime/tsup.config.ts: removed@objectstack/driver-tursofromexternal.packages/spec/src/contracts/index.ts: stopped re-exportingturso-platform.js.packages/spec/src/data/index.ts: stopped re-exportingdriver/turso-multi-tenant.zod.
If you used
libsql://URLs or@objectstack/driver-tursodirectly, either:- Switch to
file:URLs (better-sqlite3 via@objectstack/driver-sql) for local/self-hosted deployments, or - Use ObjectStack Cloud, which ships the Turso driver as part of the commercial distribution.
-
3a630b6: Split organization-scoping from
@objectstack/plugin-securityinto a new@objectstack/plugin-org-scopingpackage.Per ADR-0002, "tenant" in ObjectStack means physical isolation (one Environment = one database, handled by
@objectstack/driver-turso's multi-tenant router). The row-levelorganization_idscoping that previously lived inside SecurityPlugin is a different concept — logical scoping inside a single DB — and now ships as its own plugin.- Removed the
multiTenantconstructor option. SecurityPlugin no longer touchesorganization_idon insert and no longer registers thesys_organizationpost-create seed pipeline. - Wildcard
current_user.organization_idRLS policies in the default permission sets are now stripped UNLESS the neworg-scopingservice is registered (i.e. unlessOrgScopingPluginis also installed). - Removed export
cloneTenantSeedData(now exposed ascloneOrgSeedDatafrom@objectstack/plugin-org-scoping). bootstrapPlatformAdmin()no longer accepts amultiTenantflag and no longer auto-creates a default organization — that behavior moved toensureDefaultOrganization()in the new plugin.
Single-tenant deployments — no action required.
Multi-tenant deployments (previously
new SecurityPlugin({ multiTenant: true })):+ import { OrgScopingPlugin } from '@objectstack/plugin-org-scoping'; import { SecurityPlugin } from '@objectstack/plugin-security'; + await kernel.use(new OrgScopingPlugin()); // MUST be BEFORE SecurityPlugin - await kernel.use(new SecurityPlugin({ multiTenant: true })); + await kernel.use(new SecurityPlugin());
The runtime's
OS_MULTI_TENANTenv switch — read by@objectstack/runtime/cloud/ArtifactKernelFactory,@objectstack/plugin-dev, and theobjectstackCLI'sserve/dev/startcommands — automatically registersOrgScopingPluginwhen set totrue, so projects driven by the CLI need no code changes. - Removed the
-
Updated dependencies [74470ad]
-
Updated dependencies [d29617e]
-
Updated dependencies [dc72172]
-
Updated dependencies [3a630b6]
-
Updated dependencies [257954d]
- @objectstack/spec@7.0.0
- @objectstack/plugin-auth@7.0.0
- @objectstack/plugin-security@7.0.0
- @objectstack/plugin-org-scoping@7.0.0
- @objectstack/core@7.0.0
- @objectstack/formula@7.0.0
- @objectstack/metadata@7.0.0
- @objectstack/objectql@7.0.0
- @objectstack/observability@7.0.0
- @objectstack/driver-memory@7.0.0
- @objectstack/driver-sql@7.0.0
- @objectstack/driver-sqlite-wasm@7.0.0
- @objectstack/rest@7.0.0
- @objectstack/service-cluster@7.0.0
- @objectstack/service-i18n@7.0.0
- @objectstack/types@7.0.0
-
bac7ae5: Fix: AI HTTP routes now see the authenticated user
HttpDispatcher.handleAI()was invoking AI route handlers with only{ body, params, query }—req.userwas alwaysundefined. This silently broke every identity-aware feature that flows through/api/v1/ai/*:- LLM-titled conversations never fired (no actor →
autoCreateConversationearly-returned → no message persistence →summarizeConversationgated onmsgs.length >= 2never tripped). - Permission-aware tool execution fell back to system context (RLS bypass).
- HITL conversation linkage lost the operator's identity.
Two root causes were fixed:
resolve-execution-context.tsonly checkedauthService.api.getSession. Modern auth plugins expose the better-auth handle lazily viaawait authService.getApi(). Now tries both.handleAI()now threads the resolvedExecutionContextintoreq.user({ userId, displayName, email, roles, permissions, organizationId }) before invoking the route handler, mirroring the shape the dispatcher-plugin already promises.
End-to-end browser verification: authenticated chat → message persisted →
summarizeConversationfires → fake-OpenAI receives the title prompt →ai_conversations.titleupdated. No code changes required in@objectstack/service-ai,assistant-routes.ts, oragent-routes.ts— they already consumedreq.usercorrectly.- @objectstack/spec@6.9.0
- @objectstack/core@6.9.0
- @objectstack/types@6.9.0
- @objectstack/metadata@6.9.0
- @objectstack/objectql@6.9.0
- @objectstack/observability@6.9.0
- @objectstack/formula@6.9.0
- @objectstack/rest@6.9.0
- @objectstack/driver-memory@6.9.0
- @objectstack/driver-sql@6.9.0
- @objectstack/driver-sqlite-wasm@6.9.0
- @objectstack/plugin-auth@6.9.0
- @objectstack/plugin-security@6.9.0
- @objectstack/service-cluster@6.9.0
- @objectstack/service-i18n@6.9.0
- LLM-titled conversations never fired (no actor →
- @objectstack/spec@6.8.1
- @objectstack/core@6.8.1
- @objectstack/types@6.8.1
- @objectstack/metadata@6.8.1
- @objectstack/objectql@6.8.1
- @objectstack/observability@6.8.1
- @objectstack/formula@6.8.1
- @objectstack/rest@6.8.1
- @objectstack/driver-memory@6.8.1
- @objectstack/driver-sql@6.8.1
- @objectstack/driver-sqlite-wasm@6.8.1
- @objectstack/plugin-auth@6.8.1
- @objectstack/plugin-security@6.8.1
- @objectstack/service-cluster@6.8.1
- @objectstack/service-i18n@6.8.1
-
50ccd9c: Fix peer-dependency version range from
workspace:*toworkspace:^to avoid forced major bumps in fixed-group releases.workspace:*expands to an exact version on publish; any minor bump of the peer then falls out of range and triggers a semver-major bump on the dependent.workspace:^expands to^x.y.zwhich correctly accepts minor bumps.Affects:
service-aipeer on@objectstack/embedder-openairuntimepeer on@objectstack/driver-turso
-
Updated dependencies [6e88f77]
-
Updated dependencies [c8b9f57]
- @objectstack/spec@6.8.0
- @objectstack/rest@6.8.0
- @objectstack/objectql@6.8.0
- @objectstack/core@6.8.0
- @objectstack/formula@6.8.0
- @objectstack/metadata@6.8.0
- @objectstack/observability@6.8.0
- @objectstack/driver-memory@6.8.0
- @objectstack/driver-sql@6.8.0
- @objectstack/driver-sqlite-wasm@6.8.0
- @objectstack/plugin-auth@6.8.0
- @objectstack/plugin-security@6.8.0
- @objectstack/service-cluster@6.8.0
- @objectstack/service-i18n@6.8.0
- @objectstack/types@6.8.0
- @objectstack/spec@6.7.1
- @objectstack/core@6.7.1
- @objectstack/types@6.7.1
- @objectstack/metadata@6.7.1
- @objectstack/objectql@6.7.1
- @objectstack/observability@6.7.1
- @objectstack/formula@6.7.1
- @objectstack/rest@6.7.1
- @objectstack/driver-memory@6.7.1
- @objectstack/driver-sql@6.7.1
- @objectstack/driver-sqlite-wasm@6.7.1
- @objectstack/plugin-auth@6.7.1
- @objectstack/plugin-security@6.7.1
- @objectstack/service-cluster@6.7.1
- @objectstack/service-i18n@6.7.1
-
c5efe15: Remove residual coupling to the (already-extracted)
@objectstack/service-cloudpackage.The cloud distribution was migrated to a separate repo a while back, but the open-core CLI still carried:
- A dynamic
import('@objectstack/service-cloud')in the boot-mode dispatch forcloud/runtimemodes. - A dev-mode auto-mount that tried to load
createSingleEnvironmentPluginfrom the cloud package (now fully covered by the built-inRuntimeConfigPlugin). - An ambient
.d.tsstub for@objectstack/service-cloud. - A leftover empty
packages/services/service-cloud/directory (only staledist/+node_modules/). - Several doc-comment references.
All gone. The open-core CLI now supports
bootMode: 'standalone'only — non-standalone modes throw a clear error pointing users to the cloud distribution. No runtime behavior change for standalone users. - A dynamic
-
4944f3a: Fix
npx @objectstack/cli startcrashing withCannot find package '@objectstack/metadata'(and friends).@objectstack/runtimedynamicallyimport()s@objectstack/metadata,@objectstack/objectql, and the storage drivers (driver-memory,driver-sql,driver-sqlite-wasm,driver-turso) fromcreateStandaloneStack/createDefaultHostConfig, but they were only listed indevDependencies— so when the package was installed from npm (rather than the workspace) these imports failed at boot.They are now declared as real
dependencies.@objectstack/driver-mongodbremains anoptionalDependencybecause the standalone stack only loads it when the user passes amongodb://URL (the failure path already has a friendly error message).Also adds a small quick-start CLI command (
objectstack start) that auto-creates~/.objectstack/{data,dist,auth-secret}, boots an empty kernel with Studio + marketplace mounted, and lets users install apps at runtime — noobjectstack.config.tsrequired. -
e0c593f: Make
@objectstack/driver-tursoan optional peer dependency so defaultnpx @objectstack/cli startno longer installs@libsql/client(~5MB + native binaries) norlibsqlnative modules.Rationale:
objectstack startdefaults tofile:URLs which route tobetter-sqlite3viadriver-sql(10–15× faster than libsql for OLTP, see benchmarks). For RAG / vector workloads,sqlite-vec(~600KB) is the recommended local backend. Turso / libsql is only useful when the user explicitly opts in vialibsql:///https:///--database-driver turso.Changes:
packages/cli/package.json: moved@objectstack/driver-tursofromdependenciesto optionalpeerDependencies(peerDependenciesMeta.optional = true). npm 7+ does not auto-install optional peers;optionalDependencieswould have still installed it.packages/runtime/package.json: same.- All three dynamic-import sites for
driver-turso(runtime/src/standalone-stack.ts,runtime/src/cloud/artifact-environment-registry.ts,cli/src/commands/serve.ts) now wrap theimport()in try/catch with an actionable error message pointing users tonpm install @objectstack/driver-turso.
Verified in
/tmp/os-sim: freshnpm install @objectstack/clino longer containsnode_modules/@libsql,node_modules/libsql, ornode_modules/@objectstack/driver-turso.objectstack startboots cleanly with better-sqlite3;--database libsql://…produces the friendly error. -
Updated dependencies [4944f3a]
-
Updated dependencies [430067b]
-
Updated dependencies [4f9e9d4]
- @objectstack/driver-sql@6.7.0
- @objectstack/spec@6.7.0
- @objectstack/driver-sqlite-wasm@6.7.0
- @objectstack/core@6.7.0
- @objectstack/formula@6.7.0
- @objectstack/metadata@6.7.0
- @objectstack/objectql@6.7.0
- @objectstack/observability@6.7.0
- @objectstack/driver-memory@6.7.0
- @objectstack/plugin-auth@6.7.0
- @objectstack/plugin-security@6.7.0
- @objectstack/rest@6.7.0
- @objectstack/service-cluster@6.7.0
- @objectstack/service-i18n@6.7.0
- @objectstack/types@6.7.0
- Updated dependencies [a49cfc2]
- @objectstack/spec@6.6.0
- @objectstack/core@6.6.0
- @objectstack/formula@6.6.0
- @objectstack/observability@6.6.0
- @objectstack/plugin-auth@6.6.0
- @objectstack/plugin-security@6.6.0
- @objectstack/rest@6.6.0
- @objectstack/service-cluster@6.6.0
- @objectstack/service-i18n@6.6.0
- @objectstack/types@6.6.0
- Updated dependencies [de239ef]
- @objectstack/plugin-auth@6.5.1
- @objectstack/spec@6.5.1
- @objectstack/core@6.5.1
- @objectstack/types@6.5.1
- @objectstack/observability@6.5.1
- @objectstack/formula@6.5.1
- @objectstack/rest@6.5.1
- @objectstack/plugin-security@6.5.1
- @objectstack/service-cluster@6.5.1
- @objectstack/service-i18n@6.5.1
- @objectstack/spec@6.5.0
- @objectstack/core@6.5.0
- @objectstack/types@6.5.0
- @objectstack/observability@6.5.0
- @objectstack/formula@6.5.0
- @objectstack/rest@6.5.0
- @objectstack/plugin-auth@6.5.0
- @objectstack/plugin-security@6.5.0
- @objectstack/service-i18n@6.5.0
- @objectstack/service-cluster@5.1.8
- Updated dependencies [f8651cc]
- Updated dependencies [f8651cc]
- Updated dependencies [0bf6f9a]
- Updated dependencies [0bf6f9a]
- @objectstack/spec@6.4.0
- @objectstack/plugin-auth@6.4.0
- @objectstack/core@6.4.0
- @objectstack/formula@6.4.0
- @objectstack/observability@6.4.0
- @objectstack/plugin-security@6.4.0
- @objectstack/rest@6.4.0
- @objectstack/service-cluster@5.1.7
- @objectstack/service-i18n@6.4.0
- @objectstack/types@6.4.0
- @objectstack/spec@6.3.0
- @objectstack/core@6.3.0
- @objectstack/types@6.3.0
- @objectstack/observability@6.3.0
- @objectstack/formula@6.3.0
- @objectstack/rest@6.3.0
- @objectstack/plugin-auth@6.3.0
- @objectstack/plugin-security@6.3.0
- @objectstack/service-i18n@6.3.0
- @objectstack/service-cluster@5.1.6
-
dbb54e1: Fix: AI streaming endpoints (e.g.
POST /api/v1/ai/assistant/chat) now actually stream Server-Sent Events instead of returning the stream descriptor JSON-serialized.The shared
sendResultBase()indispatcher-plugin.tspreviously had a// pass through as JSON for nowTODO, so any dispatcher route whoseresult.resultwas a stream descriptor ({ type: 'stream', events, headers, ... }) would respond with a literal{"type":"stream", "events":{},"vercelDataStream":true,...}body — breaking@object-ui/plugin-chatbotand any other Vercel-AI-SDK consumer.The dispatcher now:
- Detects
{ type: 'stream' | stream: true, events, headers? }shapes. - Applies the route-provided headers (defaults to
text/event-stream/no-cache/keep-alivewhen none are supplied). - Performs an empty
res.write('')synchronously so the Hono adapter'sisStreamingflag flips before the route handler resolves (the adapter would otherwise close the body before the first async chunk lands). - Drains the
AsyncIterable<string>of pre-encoded SSE chunks in the background, callingres.end()when the iterator finishes or errors.
Non-stream
result.resultpayloads keep the existing JSON behaviour. - Detects
-
Updated dependencies [b4c74a9]
-
Updated dependencies [b4c74a9]
- @objectstack/plugin-auth@6.2.0
- @objectstack/spec@6.2.0
- @objectstack/core@6.2.0
- @objectstack/formula@6.2.0
- @objectstack/observability@6.2.0
- @objectstack/plugin-security@6.2.0
- @objectstack/rest@6.2.0
- @objectstack/service-cluster@5.1.5
- @objectstack/service-i18n@6.2.0
- @objectstack/types@6.2.0
- @objectstack/spec@6.1.1
- @objectstack/core@6.1.1
- @objectstack/types@6.1.1
- @objectstack/observability@6.1.1
- @objectstack/formula@6.1.1
- @objectstack/rest@6.1.1
- @objectstack/plugin-auth@6.1.1
- @objectstack/plugin-security@6.1.1
- @objectstack/service-i18n@6.1.1
- @objectstack/service-cluster@5.1.4
- Updated dependencies [93c0589]
- @objectstack/spec@6.1.0
- @objectstack/core@6.1.0
- @objectstack/formula@6.1.0
- @objectstack/observability@6.1.0
- @objectstack/plugin-auth@6.1.0
- @objectstack/plugin-security@6.1.0
- @objectstack/rest@6.1.0
- @objectstack/service-cluster@5.1.3
- @objectstack/service-i18n@6.1.0
- @objectstack/types@6.1.0
-
944f187: # v5.0 —
project→environmenthard renameThe runtime concept previously called "project" (per-tenant business workspace; Org → Project → Branch hierarchy; per-project ObjectKernel, per-project DB, per-project artifact) is now uniformly called "environment".
This is a hard rename with no aliases, deprecation shims, or compatibility layer. Upgrade requires a coordinated update of CLI, runtime, server, and any clients calling the REST API.
Note: "project" in the npm / monorepo sense (the framework itself,
package.json, tsconfig project references, vitestprojectsconfig) is unchanged.- Flags renamed:
--project/-p→--environment/-e(os publish,os rollback)--project-id→--environment-id(os dev)
- Default local env id:
proj_local→env_local. - Env var:
OS_PROJECT_ID→OS_ENVIRONMENT_ID. - Command group renamed:
os projects ...→os environments ...(bind,create,list,show,switch). - Persisted auth-config key:
activeProjectId→activeEnvironmentId.
- Scoped routes:
/api/v1/projects/:projectId/...→/api/v1/environments/:environmentId/.... - Cloud control-plane routes:
/api/v1/cloud/projects/...→/api/v1/cloud/environments/...(including/cloud/environments/:id/artifact,/cloud/environments/:id/metadata,/cloud/environments/:id/credentials/rotate, etc.). - Header:
X-Project-Id(and lowercasex-project-id) →X-Environment-Id(x-environment-id). - Route param name in handlers:
req.params.projectId→req.params.environmentId. - Hostname-routing and tenant-resolution code-paths use
environmentIdend-to-end.
- Exported symbols (no aliases):
createSystemProjectPlugin→createSystemEnvironmentPluginSYSTEM_PROJECT_ID→SYSTEM_ENVIRONMENT_IDProjectArtifactSchema→EnvironmentArtifactSchemaPROJECT_ARTIFACT_SCHEMA_VERSION→ENVIRONMENT_ARTIFACT_SCHEMA_VERSIONObjectOSProjectPlugin→ObjectOSEnvironmentPlugincreateSingleProjectPlugin→createSingleEnvironmentPlugin
- Plugin identifier strings:
com.objectstack.runtime.objectos-project→objectos-environmentcom.objectstack.studio.single-project→single-environmentcom.objectstack.multi-project→multi-environmentcom.objectstack.runtime.system-project→system-environment
- Provisioning hook:
provisionSystemProject→provisionSystemEnvironment.
- Column renames on
sys_metadataandsys_metadata_history:project_id→environment_id. - Column renames on
sys_activity:project_id→environment_id(plus index). - Object renames in platform-objects metadata:
sys_project→sys_environment(lookup targets),sys_project_member→sys_environment_member,sys_project_credential→sys_environment_credential. - Auth-context field:
active_project_id→active_environment_id. - JSON schemas under
packages/spec/json-schema/system/:ProjectArtifact*.json→EnvironmentArtifact*.json(regenerated at build).
A new migration
migrateProjectIdToEnvironmentId(packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts) auto-runs fromDatabaseLoader.ensureSchema()on bootstrap and rewrites any existingproject_idcolumn onsys_metadata/sys_metadata_historytoenvironment_id(idempotent, best-effort). Existing rows are preserved.The legacy reverse migration
migrateEnvIdToProjectIdis retained verbatim for historical / disaster-recovery use; it is not auto-run.-os publish --project proj_xyz +os publish --environment env_xyz -curl -H "X-Project-Id: env_xyz" https://api.example.com/api/v1/data/customer +curl -H "X-Environment-Id: env_xyz" https://api.example.com/api/v1/data/customer -OS_PROJECT_ID=env_xyz os dev +OS_ENVIRONMENT_ID=env_xyz os dev -import { createSystemProjectPlugin, SYSTEM_PROJECT_ID } from "@objectstack/runtime"; +import { createSystemEnvironmentPlugin, SYSTEM_ENVIRONMENT_ID } from "@objectstack/runtime"; -import { ProjectArtifactSchema } from "@objectstack/spec"; +import { EnvironmentArtifactSchema } from "@objectstack/spec";
If you maintain a Cloud control-plane deployment, the
cloudrepository must be updated in lockstep to pick up the new plugin identifier strings (single-environment,multi-environment,objectos-environment). - Flags renamed:
- Updated dependencies [629a716]
- Updated dependencies [dbc4f7d]
- Updated dependencies [944f187]
- @objectstack/spec@6.0.0
- @objectstack/rest@6.0.0
- @objectstack/core@6.0.0
- @objectstack/formula@6.0.0
- @objectstack/observability@6.0.0
- @objectstack/plugin-auth@6.0.0
- @objectstack/plugin-security@6.0.0
- @objectstack/service-cluster@5.1.2
- @objectstack/service-i18n@6.0.0
- @objectstack/types@6.0.0
-
b806f58: Scope
sys_uservisibility to fellow organization members.The default RLS policy on
sys_userwasid = current_user.id, which meant @-mention pickers, owner/assignee lookups, reviewer selectors and the user roster all returned just the current user. The RLS compiler doesn't support subqueries, so aid IN (SELECT user_id FROM sys_member ...)policy isn't expressible.This change:
- Pre-resolves
org_user_ids(the IDs of all users in the active org) intoExecutionContextin all three REST entry-point resolvers (@objectstack/rest,@objectstack/runtime,@objectstack/plugin-hono-server). - Adds the field to
ExecutionContextSchemaso it survives Zod parsing. - Adds an
org_user_idsfield to the RLS compiler's user context. - Adds a new
sys_user_org_memberspolicy (id IN (current_user.org_user_ids)) to bothmember_defaultandviewer_readonlypermission sets, alongside the existingsys_user_selfpolicy. The RLS compiler OR-combines them, so users see themselves AND their org collaborators.
Capped at 1000 members per request. Large enterprises should plug in a directory cache or split per workspace.
- Pre-resolves
-
Updated dependencies [bab2b20]
-
Updated dependencies [fa011d8]
-
Updated dependencies [b806f58]
- @objectstack/spec@5.2.0
- @objectstack/plugin-security@5.2.0
- @objectstack/rest@5.2.0
- @objectstack/plugin-auth@5.2.0
- @objectstack/core@5.2.0
- @objectstack/formula@5.2.0
- @objectstack/observability@5.2.0
- @objectstack/service-cluster@5.1.1
- @objectstack/service-i18n@5.2.0
- @objectstack/types@5.2.0
- Updated dependencies [75f4ee6]
- Updated dependencies [823d559]
- @objectstack/spec@5.1.0
- @objectstack/core@5.1.0
- @objectstack/formula@5.1.0
- @objectstack/observability@5.1.0
- @objectstack/plugin-auth@5.1.0
- @objectstack/plugin-security@5.1.0
- @objectstack/rest@5.1.0
- @objectstack/service-i18n@5.1.0
- @objectstack/types@5.1.0
-
5e9dcb4: BREAKING — metadata: remove
projectandbranchfromMetaRefThe metadata layer no longer models project or branch. Customisation is now scoped purely to organisation. Project remains exclusively as an artifact packaging concept (the
objectstack.jsonbundle envelope); branching is left to Git.What changed:
MetaRefis now{ org, type, name, version? }(was{ org, project, branch, type, name, version? }).refKey()is the two segment string${org}/${type}/${name}(was five segments).MetadataItem.seqis monotonic per org (was per branch).BranchRef,MergeStrategy,MergeResulttypes and the optionalfork/mergemethods onMetadataRepositoryare removed.ListFilter/WatchFilter/HistoryOptionsno longer acceptprojectorbranch.FileSystemRepositorydisk layout simplified to<root>/<type>/<name>.json(was<root>/<project>/<branch>/<type>/<name>.json); change-log path is now.objectstack/.log/main.jsonlregardless of any branch concept. Constructor no longer acceptsproject/branch.SysMetadataRepository: removedprojectLabel/branchLabeloptions; thesys_metadataschema'sproject_id/branchcolumns (if present) are ignored. A future major release willDROPthem.MetadataManager.setRepository(repo, opts)no longer takes an opts object withbranch.
Migration:
-const ref = { org: 'acme', project: 'crm', branch: 'main', type: 'view', name: 'home' }; +const ref = { org: 'acme', type: 'view', name: 'home' }; -new FileSystemRepository({ root, org: 'acme', project: 'crm', branch: 'main' }); +new FileSystemRepository({ root, org: 'acme' });
Existing
sys_metadatarows continue to load; the deprecated columns are ignored at read time.
-
96ad4df: Fix dev-mode HMR data-reload for
*.view.ts/*.flow.tssource-file edits.Three coordinated fixes close the long-standing gap where editing a declarative-metadata source file in dev (e.g.
case.view.ts) would recompiledist/objectstack.jsonbut the running server kept serving the stale boot-time value:-
@objectstack/objectql—ObjectStackProtocolImplementation.getMetaItemnow consultsMetadataService(HMR-aware) before the in-memorySchemaRegistry(boot-time cache). Previously the registry shadowed freshly-registered values:manager.register('view','case',newDef)updated MetadataManager butgetMetaItemreturned the stale registry copy because step 2 (registry) ran before step 3 (service). Reordered to "1. sys_metadata overlay → 2. MetadataService → 3. SchemaRegistry". -
@objectstack/runtime—createStandaloneStacknow enables theMetadataPluginartifact-file watcher in non-production environments (NODE_ENV !== 'production'). Previously hard-coded towatch: false, leaving nothing watchingdist/objectstack.jsonwhen the CLI dev mode recompiled it. -
@objectstack/metadata&@objectstack/metadata-fs— Both chokidar watchers now useusePolling: trueto avoidfs.watchEMFILE on macOS / busy dev hosts where the native file-descriptor pool can be exhausted by other long-running node processes.
With these three changes:
- CLI edits source → recompile artifact (~400ms)
- Server's polling chokidar detects artifact change →
_loadFromLocalFile _loadFromLocalFilecallsmanager.register(type, name, item)- MetadataService now has the fresh value
- Read path returns the fresh value via the new step-2 lookup
- Studio SSE listeners re-render
-
-
df18ae9: Fix dev-mode HMR data-reload for view metadata.
MetadataPlugin._parseAndRegisterArtifactpreviously required a top-levelnameon every artifact item and silently skipped those without one. View bundles in the compiled artifact carry no top-levelname(their identity is the target object, encoded underlist.data.object/form.data.object— same pattern used byObjectQL.SchemaRegistry'sresolveMetadataItemName). As a result, artifact-loaded views never reachedMetadataManager, and HMR file pushes never affected the read path: API responses kept returning the boot-timeSchemaRegistrycopy.This change derives the registration key from
list.data.object(orform.data.object) when no top-levelnameis present, mirroring the ObjectQL convention.Also splits the
MetadataPluginwatch flag into two independent options so dev mode can enable artifact-file HMR without paying the cost of the source-file scanner:watch— controlsNodeMetadataManager's recursive source scan (defaultfalse; turning it on in artifact mode would polling-scan the entire project root includingnode_modules).artifactWatch— controls the cheap single-file polling watcher on the compiled artifact (dist/objectstack.json). The standalone stack enables this automatically whenNODE_ENV !== 'production'.
-
Updated dependencies [5cfdc85]
-
Updated dependencies [2f9073a]
- @objectstack/rest@5.0.0
- @objectstack/spec@5.0.0
- @objectstack/plugin-auth@5.0.0
- @objectstack/plugin-security@5.0.0
- @objectstack/core@5.0.0
- @objectstack/formula@5.0.0
- @objectstack/observability@5.0.0
- @objectstack/service-i18n@5.0.0
- @objectstack/types@5.0.0
- Updated dependencies [2869891]
- @objectstack/spec@4.2.0
- @objectstack/rest@4.2.0
- @objectstack/core@4.2.0
- @objectstack/formula@4.2.0
- @objectstack/plugin-auth@4.2.0
- @objectstack/plugin-security@4.2.0
- @objectstack/service-i18n@4.2.0
- @objectstack/types@4.2.0
- @objectstack/spec@4.1.1
- @objectstack/core@4.1.1
- @objectstack/types@4.1.1
- @objectstack/formula@4.1.1
- @objectstack/rest@4.1.1
- @objectstack/plugin-auth@4.1.1
- @objectstack/plugin-security@4.1.1
- @objectstack/service-i18n@4.1.1
-
96fb108: Artifact-first boot:
objectstack start(andobjectstack serve) now boot directly from a compileddist/objectstack.jsonwhen noobjectstack.config.tsis present.@objectstack/runtimeexportscreateDefaultHostConfig()andresolveDefaultArtifactPath()— a standalone-only default host that wrapscreateStandaloneStack()and surfaces the artifact'srequires/objects/manifest. No dependency on@objectstack/service-cloud.objectstack startacceptsOS_ARTIFACT_PATHas a file path or anhttp(s)://URL. New flags--artifact,--database,--database-driver,--database-auth-token,--auth-secret,--project-id,--portlet you specify all runtime conditions on the command line (each overrides the matching env var).objectstack devaccepts the same runtime-override flags. When--artifactis supplied, the auto-compile step is skipped and the dev server boots the supplied artifact directly — noobjectstack.config.tsrequired in cwd.objectstack startno longer mounts Studio / Account / Console by default — those are dev/admin surfaces. Pass--uito opt back in.objectstack servefalls back to the default host config when the config file is missing but an artifact is resolvable.apps/objectos(cloud / multi-project) is unchanged.
-
70db902: Add production observability primitives.
createDispatcherPluginnow exposes anobservabilityconfig that auto-instruments every mounted route with:- Request-id propagation:
X-Request-Idecho +req.requestId(honors incoming header when well-formed, mintsreq_<uuid>otherwise). http_requests_total{method,route,status}counter.http_request_duration_ms{method,route}histogram.http_request_errors_total{method,route}counter.- Error reporter call for 5xx (4xx are intentionally tracked via metrics only, not reported, to keep APM signal:noise high).
All defaults are no-op (zero overhead). Hosts plug their own
MetricsRegistry(Prometheus / OTel) andErrorReporter(Sentry / Datadog) — seedocs/OBSERVABILITY.mdfor adapter recipes and the go-live checklist.Standalone primitives also exported for adapter-layer use:
extractRequestId,resolveRequestId,parseTraceparent,formatTraceparent,InMemoryMetricsRegistry,InMemoryErrorReporter,instrumentRouteHandler. - Request-id propagation:
-
70db902: Add production HTTP hardening primitives.
createDispatcherPluginnow sends conservative security response headers by default (CSP / X-Content-Type-Options / X-Frame-Options / Referrer-Policy / Permissions-Policy / Cross-Origin-Resource-Policy). HSTS is opt-in.Caller can disable with
securityHeaders: false(e.g., when an upstream reverse proxy already injects them) or customize per-header viaSecurityHeadersOptions.Also exports a standalone token-bucket
RateLimiterwith a pluggableRateLimitStoreinterface (in-memory default; trivially backed by Redis) and curatedDEFAULT_RATE_LIMITSfor auth / write / read buckets. The limiter is NOT auto-wired into the dispatcher — adapter-layer wire-up (Fastify / Hono / Express) is recommended for proper IP/key extraction; seedocs/HARDENING.mdfor recipes.
- Updated dependencies [2108c30]
- Updated dependencies [23db640]
- Updated dependencies [d3b455f]
- @objectstack/spec@4.1.0
- @objectstack/plugin-security@4.1.0
- @objectstack/core@4.1.0
- @objectstack/formula@4.1.0
- @objectstack/plugin-auth@4.1.0
- @objectstack/rest@4.1.0
- @objectstack/service-i18n@4.1.0
- @objectstack/types@4.1.0
- 15e0df6: chore: unify all package versions to a single patch release
- Updated dependencies [15e0df6]
- @objectstack/spec@4.0.5
- @objectstack/core@4.0.5
- @objectstack/types@4.0.5
- @objectstack/formula@4.0.5
- @objectstack/rest@4.0.5
- Updated dependencies [326b66b]
- @objectstack/spec@4.0.4
- @objectstack/core@4.0.4
- @objectstack/rest@4.0.4
- @objectstack/types@4.0.4
- @objectstack/spec@4.0.3
- @objectstack/core@4.0.3
- @objectstack/types@4.0.3
- @objectstack/rest@4.0.3
- Updated dependencies [5f659e9]
- @objectstack/spec@4.0.2
- @objectstack/core@4.0.2
- @objectstack/rest@4.0.2
- @objectstack/types@4.0.2
-
f08ffc3: Fix discovery API endpoint routing and protocol consistency.
Discovery route standardization:
- All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at
{prefix}/discoveryinstead of{prefix}root. .well-known/objectstackredirects now point to{prefix}/discovery.- Client
connect()fallback URL changed from/api/v1to/api/v1/discovery. - Runtime dispatcher handles both
/discovery(standard) and/(legacy) for backward compatibility.
Schema & route alignment:
- Added
storage(service:file-storage) andfeed(service:data) routes toDEFAULT_DISPATCHER_ROUTES. - Added
feedanddiscoveryfields toApiRoutesSchema. - Unified
GetDiscoveryResponseSchemawithDiscoverySchemaas single source of truth. - Client
getRoute('feed')fallback updated from/api/v1/datato/api/v1/feed.
Type safety:
- Extracted
ApiRouteTypefromApiRouteskeys for type-safe client route resolution. - Removed
as anytype casting in client route access.
- All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at
-
e0b0a78: Deprecate DataEngineQueryOptions in favor of QueryAST-aligned EngineQueryOptions.
Engine, Protocol, and Client now use standard QueryAST parameter names:
filter→whereselect→fieldssort→orderByskip→offsetpopulate→expandtop→limit
The old DataEngine* schemas and types are preserved with
@deprecatedmarkers for backward compatibility. -
Updated dependencies [f08ffc3]
-
Updated dependencies [e0b0a78]
- @objectstack/spec@4.0.0
- @objectstack/core@4.0.0
- @objectstack/rest@4.0.0
- @objectstack/types@4.0.0
- @objectstack/spec@3.3.1
- @objectstack/core@3.3.1
- @objectstack/types@3.3.1
- @objectstack/rest@3.3.1
- @objectstack/spec@3.3.0
- @objectstack/core@3.3.0
- @objectstack/types@3.3.0
- @objectstack/rest@3.3.0
- @objectstack/spec@3.2.9
- @objectstack/core@3.2.9
- @objectstack/types@3.2.9
- @objectstack/rest@3.2.9
- @objectstack/spec@3.2.8
- @objectstack/core@3.2.8
- @objectstack/types@3.2.8
- @objectstack/rest@3.2.8
- @objectstack/spec@3.2.7
- @objectstack/core@3.2.7
- @objectstack/types@3.2.7
- @objectstack/rest@3.2.7
- @objectstack/spec@3.2.6
- @objectstack/core@3.2.6
- @objectstack/types@3.2.6
- @objectstack/rest@3.2.6
- @objectstack/spec@3.2.5
- @objectstack/core@3.2.5
- @objectstack/types@3.2.5
- @objectstack/rest@3.2.5
- @objectstack/spec@3.2.4
- @objectstack/core@3.2.4
- @objectstack/types@3.2.4
- @objectstack/rest@3.2.4
- @objectstack/spec@3.2.3
- @objectstack/core@3.2.3
- @objectstack/types@3.2.3
- @objectstack/rest@3.2.3
- Updated dependencies [46defbb]
- @objectstack/spec@3.2.2
- @objectstack/core@3.2.2
- @objectstack/rest@3.2.2
- @objectstack/types@3.2.2
- Updated dependencies [850b546]
- @objectstack/spec@3.2.1
- @objectstack/core@3.2.1
- @objectstack/rest@3.2.1
- @objectstack/types@3.2.1
- Updated dependencies [5901c29]
- @objectstack/spec@3.2.0
- @objectstack/core@3.2.0
- @objectstack/rest@3.2.0
- @objectstack/types@3.2.0
- Updated dependencies [953d667]
- @objectstack/spec@3.1.1
- @objectstack/core@3.1.1
- @objectstack/rest@3.1.1
- @objectstack/types@3.1.1
- Updated dependencies [0088830]
- @objectstack/spec@3.1.0
- @objectstack/core@3.1.0
- @objectstack/rest@3.1.0
- @objectstack/types@3.1.0
- Updated dependencies [92d9d99]
- @objectstack/spec@3.0.11
- @objectstack/core@3.0.11
- @objectstack/rest@3.0.11
- @objectstack/types@3.0.11
- Updated dependencies [d1e5d31]
- @objectstack/spec@3.0.10
- @objectstack/core@3.0.10
- @objectstack/rest@3.0.10
- @objectstack/types@3.0.10
- Updated dependencies [15e0df6]
- @objectstack/spec@3.0.9
- @objectstack/core@3.0.9
- @objectstack/rest@3.0.9
- @objectstack/types@3.0.9
- Updated dependencies [5a968a2]
- @objectstack/spec@3.0.8
- @objectstack/core@3.0.8
- @objectstack/rest@3.0.8
- @objectstack/types@3.0.8
- Updated dependencies [0119bd7]
- Updated dependencies [5426bdf]
- @objectstack/spec@3.0.7
- @objectstack/core@3.0.7
- @objectstack/rest@3.0.7
- @objectstack/types@3.0.7
- Updated dependencies [5df254c]
- @objectstack/spec@3.0.6
- @objectstack/core@3.0.6
- @objectstack/rest@3.0.6
- @objectstack/types@3.0.6
- Updated dependencies [23a4a68]
- @objectstack/spec@3.0.5
- @objectstack/core@3.0.5
- @objectstack/rest@3.0.5
- @objectstack/types@3.0.5
- Updated dependencies [d738987]
- @objectstack/spec@3.0.4
- @objectstack/core@3.0.4
- @objectstack/rest@3.0.4
- @objectstack/types@3.0.4
- c7267f6: Patch release for maintenance updates and improvements.
- Updated dependencies [c7267f6]
- @objectstack/spec@3.0.3
- @objectstack/core@3.0.3
- @objectstack/types@3.0.3
- @objectstack/rest@3.0.3
- Updated dependencies [28985f5]
- @objectstack/spec@3.0.2
- @objectstack/core@3.0.2
- @objectstack/rest@3.0.2
- @objectstack/types@3.0.2
- Updated dependencies [389725a]
- @objectstack/spec@3.0.1
- @objectstack/core@3.0.1
- @objectstack/rest@3.0.1
- @objectstack/types@3.0.1
- Release v3.0.0 — unified version bump for all ObjectStack packages.
- Updated dependencies
- @objectstack/spec@3.0.0
- @objectstack/core@3.0.0
- @objectstack/types@3.0.0
- @objectstack/rest@3.0.0
- Updated dependencies
- @objectstack/spec@2.0.7
- @objectstack/core@2.0.7
- @objectstack/rest@2.0.7
- @objectstack/types@2.0.7
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.6
- @objectstack/core@2.0.6
- @objectstack/types@2.0.6
- @objectstack/rest@2.0.6
- Updated dependencies
- @objectstack/spec@2.0.5
- @objectstack/core@2.0.5
- @objectstack/rest@2.0.5
- @objectstack/types@2.0.5
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.4
- @objectstack/core@2.0.4
- @objectstack/types@2.0.4
- @objectstack/rest@2.0.4
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.3
- @objectstack/core@2.0.3
- @objectstack/types@2.0.3
- @objectstack/rest@2.0.3
- Updated dependencies [1db8559]
- @objectstack/spec@2.0.2
- @objectstack/core@2.0.2
- @objectstack/rest@2.0.2
- @objectstack/types@2.0.2
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.1
- @objectstack/core@2.0.1
- @objectstack/types@2.0.1
- @objectstack/rest@2.0.1
- Updated dependencies [38e5dd5]
- Updated dependencies [38e5dd5]
- @objectstack/spec@2.0.0
- @objectstack/core@2.0.0
- @objectstack/rest@2.0.0
- @objectstack/types@2.0.0
- chore: add Vercel deployment configs, simplify console runtime configuration
- Updated dependencies
- @objectstack/spec@1.0.12
- @objectstack/core@1.0.12
- @objectstack/types@1.0.12
- @objectstack/spec@1.0.11
- @objectstack/core@1.0.11
- @objectstack/types@1.0.11
- Updated dependencies [10f52e1]
- @objectstack/core@1.0.10
- @objectstack/spec@1.0.10
- @objectstack/types@1.0.10
- @objectstack/spec@1.0.9
- @objectstack/core@1.0.9
- @objectstack/types@1.0.9
- @objectstack/spec@1.0.8
- @objectstack/core@1.0.8
- @objectstack/types@1.0.8
- ebdf787: feat: implement standard service discovery via
/.well-known/objectstack- @objectstack/spec@1.0.7
- @objectstack/core@1.0.7
- @objectstack/types@1.0.7
- Updated dependencies [a7f7b9d]
- @objectstack/spec@1.0.6
- @objectstack/core@1.0.6
- @objectstack/types@1.0.6
- b1d24bd: refactor: migrate build system from tsc to tsup for faster builds
- Replaced
tscwithtsup(using esbuild) across all packages - Added shared
tsup.config.tsin workspace root - Added
tsupas workspace dev dependency - significantly improved build performance
- Replaced
- 877b864: fix: add SPA fallback to hono, fix msw context binding, improve runtime resilience, and fix client-react build types
- Updated dependencies [b1d24bd]
- @objectstack/core@1.0.5
- @objectstack/types@1.0.5
- @objectstack/spec@1.0.5
- @objectstack/spec@1.0.4
- @objectstack/core@1.0.4
- @objectstack/types@1.0.4
- fb2eabd: fix: resolve "process is not defined" runtime error in browser environments by adding safe environment detection and polyfills
- Updated dependencies [fb2eabd]
- @objectstack/core@1.0.3
- @objectstack/spec@1.0.3
- @objectstack/types@1.0.3
-
a0a6c85: Infrastructure and development tooling improvements
- Add changeset configuration for automated version management
- Add comprehensive GitHub Actions workflows (CI, CodeQL, linting, releases)
- Add development configuration files (.cursorrules, .github/prompts)
- Add documentation files (ARCHITECTURE.md, CONTRIBUTING.md, workflows docs)
- Update test script configuration in package.json
- Add @objectstack/cli to devDependencies for better development experience
-
109fc5b: Unified patch release to align all package versions.
-
Updated dependencies [a0a6c85]
-
Updated dependencies [109fc5b]
- @objectstack/spec@1.0.2
- @objectstack/core@1.0.2
- @objectstack/types@1.0.2
- Fix TypeScript error in http-dispatcher tests to resolve CI build failures.
- @objectstack/spec@1.0.1
- @objectstack/core@1.0.1
- @objectstack/types@1.0.1
- Major version release for ObjectStack Protocol v1.0.
- Stabilized Protocol Definitions
- Enhanced Runtime Plugin Support
- Fixed Type Compliance across Monorepo
- Updated dependencies
- @objectstack/spec@1.0.0
- @objectstack/core@1.0.0
- @objectstack/types@1.0.0
- Updated dependencies
- @objectstack/spec@0.9.2
- @objectstack/core@0.9.2
- @objectstack/types@0.9.2
- Patch release for maintenance and stability improvements. All packages updated with unified versioning.
- Updated dependencies
- @objectstack/spec@0.9.1
- @objectstack/core@0.9.1
- @objectstack/types@0.9.1
- Updated dependencies [555e6a7]
- @objectstack/spec@0.8.2
- @objectstack/core@0.8.2
- @objectstack/types@0.8.2
- @objectstack/spec@0.8.1
- @objectstack/core@0.8.1
- @objectstack/types@0.8.1
-
This release includes a major upgrade to the core validation engine (Zod v4) and aligns all protocol definitions with stricter type safety.
- Updated dependencies
- @objectstack/spec@1.0.0
- @objectstack/core@1.0.0
- @objectstack/types@1.0.0
- fb41cc0: Patch release: Updated documentation and JSON schemas
- Updated dependencies [fb41cc0]
- @objectstack/spec@0.7.2
- @objectstack/core@0.7.2
- @objectstack/types@0.7.2
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@0.7.1
- @objectstack/types@0.7.1
- @objectstack/core@0.7.1
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@0.6.1
- @objectstack/types@0.6.1
- @objectstack/core@0.6.1
-
b2df5f7: Unified version bump to 0.5.0
- Standardized all package versions to 0.5.0 across the monorepo
- Fixed driver-memory package.json paths for proper module resolution
- Ensured all packages are in sync for the 0.5.0 release
- Updated dependencies [b2df5f7]
- @objectstack/spec@0.6.0
- @objectstack/objectql@0.6.0
- @objectstack/types@0.6.0
- @objectstack/core@0.6.0
- Unify all package versions to 0.4.2
- Updated dependencies
- @objectstack/spec@0.4.2
- @objectstack/objectql@0.4.2
- @objectstack/types@0.4.2
-
Version synchronization and dependency updates
- Synchronized plugin-msw version to 0.4.1
- Updated runtime peer dependency versions to ^0.4.1
- Fixed internal dependency version mismatches
-
Updated dependencies
- @objectstack/spec@0.4.1
- @objectstack/types@0.4.1
- @objectstack/objectql@0.4.1
- Release version 0.4.0
-
Workflow and configuration improvements
- Enhanced GitHub workflows for CI, release, and PR automation
- Added comprehensive prompt templates for different protocol areas
- Improved project documentation and automation guides
- Updated changeset configuration
- Added cursor rules for better development experience
-
Updated dependencies
- @objectstack/spec@0.3.3
- @objectstack/objectql@0.3.3
- @objectstack/types@0.3.3
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/objectql@0.3.2
- @objectstack/spec@0.3.2
- @objectstack/types@0.3.2
- Organize zod schema files by folder structure and improve project documentation
- @objectstack/spec@0.3.1
- @objectstack/objectql@0.3.1
- @objectstack/types@0.3.1
- Updated dependencies
- @objectstack/spec@1.0.0
- @objectstack/objectql@1.0.0
- @objectstack/types@1.0.0
-
Initial release of ObjectStack Protocol & Specification packages
This is the first public release of the ObjectStack ecosystem, providing:
- Core protocol definitions and TypeScript types
- ObjectQL query language and runtime
- Memory driver for in-memory data storage
- Client library for interacting with ObjectStack
- Hono server plugin for REST API endpoints
- Complete JSON schema generation for all specifications
- Updated dependencies
- @objectstack/spec@0.2.0
- @objectstack/types@0.2.0
- @objectstack/objectql@0.2.0
- Remove debug logs from registry and protocol modules
- Updated dependencies
- @objectstack/spec@0.1.2
- @objectstack/objectql@0.1.1
- @objectstack/types@0.1.1