Changelog & Versioning
How ObjectOS is versioned, what changes between releases, and what's supported.
Versioning policy
ObjectOS follows Semantic Versioning: MAJOR.MINOR.PATCH.
| Version bump | What it means | What to do |
|---|---|---|
Patch (14.7.0 → 14.7.1) | Bug fixes, no behavior change | Roll forward, no app changes needed |
Minor (14.6 → 14.7) | New features, backward-compatible | Roll forward, optionally adopt new features |
Major (13 → 14) | Breaking changes documented in release notes | Read the migration guide before upgrading |
All @objectstack/* packages release together with a synchronized
version number — they're tested as a matrix, not independently.
Compatibility matrix
| Component | Compatibility rule |
|---|---|
| ObjectOS image ↔ compiled artifact | Same minor version. A 14.7.x image runs a 14.7.x artifact; a 14.7 artifact may use features unavailable in a 14.0 image. The protocol handshake (PROTOCOL_VERSION, 12.0+) rejects incompatible packages at install time. |
| ObjectOS ↔ CLI | Same minor version recommended. The CLI in npm i -g writes scaffolds pinned to its own version. |
| ObjectOS ↔ database driver | Driver pinned by image build; verify Postgres ≥ 13 / MongoDB ≥ 5 / Turso (any current). |
| Node.js | 20 LTS or newer. 22 LTS recommended for new deployments. |
Support windows
| Branch | Status | Until |
|---|---|---|
| 14.x (current) | Active development; new features and fixes | At least 12 months after 15.0 ships |
| 13.x | Security fixes only | EOL on 15.0 release |
| ≤ 12.x | Unsupported | Already EOL |
Critical security fixes are backported to the current and previous
major. Everything else lands on main.
Release notes
Released ObjectOS versions and their CHANGELOG entries are published at:
- npm:
@objectstack/runtime - GitHub: github.com/objectstack-ai/objectos/releases
- Source CHANGELOG:
CHANGELOG.md - Long-form release notes:
RELEASE_NOTES.md
Subscribe to releases on GitHub to get notified.
Recent highlights
16.0
@objectstack 16.0.0 converges the developer surface and makes
declared metadata honest: one blessed name for the caller's org, real
multi-approver governance, time-relative automations that actually fire,
and a platform-wide sweep that turns silently-ignored metadata into loud
errors at authoring time. Full notes:
docs.objectstack.ai/docs/releases.
What moved:
tenantIdalias removed from hook/actionctx(16.0, breaking) — read the caller's org asctx.user.organizationId/ctx.session.organizationIdin every*.hook.ts/*.action.tsbody (the value is unchanged;ctx.userisundefinedfor system writes). The driver-layer tenancy axis (ExecutionContext.tenantId) is a different concept and is untouched.- Approvals: quorum, per-group sign-off (会签), and a metadata-driven
inbox — approval steps support M-of-N quorum (
minApprovals) and one-approver-from-each-group steps, tallied against an open-time snapshot with OOO substitution; a single rejection still vetoes, and thresholds clamp so misconfiguration can never deadlock. Decisions carry file attachments, progress ("2 of 3 · finance pending") is computed server-side, and approve / reject / reassign / send-back / request-info / remind / recall / resubmit are declaredtype:'api'actions onsys_approval_request— the Console renders them through its generic action runtime instead of hand-written buttons. - Time-relative automations — a flow start node can declare
timeRelative(offsetDays: [60, 30, 7]orwithinDays); a daily sweep launches the flow once per matching record, so "remind me 60 days before the contract ends" finally just works — no more date-equality conditions that only fire on a lucky edit. - Filtered roll-ups —
summaryOperations.filterlets a parent total aggregate only the matching child rows, and re-aggregates when a child moves in or out of the predicate. - Strict dashboard widgets (16.0, breaking) —
DashboardWidgetSchemais.strict(): an undeclared key (typo or removed inline-analytics key) is a parse error naming the key and pointing at the dataset shape (dataset+dimensions+values) instead of silently rendering nothing. - MCP: stdio gets an identity, agents get a linter — stdio
auto-start moves to its own
OS_MCP_STDIO_ENABLEDswitch (default off) and requires anOS_MCP_STDIO_API_KEY=osk_...principal, fail-closed, resolved through the same authorization chain as HTTP (RLS/FLS/tenant apply; nosystembypass). A newvalidate_expressiontool lets agents lint a formula against the real object schema before saving it. - Enforce-or-remove sweep (16.0, breaking) — declared-but-unenforced
metadata is now loud: dead field/object/agent props are removed or
tombstoned with guidance, hook events collapse 18 → 8, validation
rules lose the never-evaluated
'delete'event, webhookundelete/apitriggers are removed, and unknownrequirescapability tokens are rejected at authoring (theaiStudio/aiSeataliases are gone — use kebab-caseai-studio/ai-seat). - Engine-owned system rows are read-only through the generic data API
(ADR-0103) — jobs, notifications, approval-runtime rows, sharing rows,
audit trails, secrets, and the like are locked to
get/list, with a fail-closed guard rejecting user-context writes through/data. Third-partysystemobjects that legitimately take user writes must declareuserActions. - Date logic stops lying —
record.due_date == today()now matches (temporal equality comparisons are rewritten to coerce the field operand), while date arithmetic in formulas (end - start + 1,today() + 30) becomes a build-time error pointing atdaysBetween/daysFromNow/addDays/addMonths— those expressions always faulted tonullat runtime anyway. - Bulk user import defaults to
passwordPolicy: 'auto'— rows with a deliverable channel (real email + wired email service, or phone + SMS invite path) are invited; only unreachable rows get a one-time temporary password (must_change_password). PasspasswordPolicy: 'none'explicitly for the old identity-only behavior; per-row outcomes are onrows[].delivery.
14.x
ObjectOS One and the bundled server shipped on @objectstack 14.7.0.
The runtime boot contract is unchanged — createStandaloneStack takes the
same artifact, environment, and database settings — but 13.0 and 14.0 both
land breaking changes in the authorization vocabulary, so review your
permission metadata before rolling forward from ≤ 12.x. Full notes:
docs.objectstack.ai/docs/releases.
What moved in 14.x:
- ADR-0090 vocabulary convergence completed (14.0, breaking) —
book.audiencegates on{ permissionSet }(was{ profile }),PortalSchema.profiles→positions,RLSUserContextSchema.role→positions(string array),sys_record_share.recipient_type: 'role'→'position'. - Object capability flags are enforced (14.0, breaking-ish) — the
enable.*flags went from parsed-but-unused to real gates.activitiesandfeedsare opt-out (defaulttrue;feeds: falserejects comments with 403FEEDS_DISABLED),trackHistorydrives the History tab, andfilesis opt-in (403FILES_DISABLEDuntilenable: { files: true }). - Member baseline lost delete (14.2, breaking) —
member_defaultno longer grants record deletion; re-grant per object through position-distributed permission sets. - Admin user management & phone auth (14.3) — direct user creation
(
POST /api/v1/auth/admin/create-user, one-time passwords with forced rotation), bulk import (rows/CSV/XLSX, dry-run, upsert), opt-in phone sign-in with SMS OTP via@objectstack/plugin-sms(Aliyun / Twilio), and ansmsnotification channel. - Data-lifecycle contract (14.4, ADR-0057) — objects declare
lifecycle(class, retention, rotation, archive); the default-on LifecycleService reaps, rotates, and archives platform data (sys_activity14 days,sys_audit_log90 days hot). Disable withOS_LIFECYCLE_DISABLED=1; tune vialifecycle.retention_overrides. 14.5 removed the per-pluginretentionDays/retentionSweepMsoptions (JobRunRetention,NotificationRetention) in favor of lifecycle declarations, and split lifecycle-classed system data into a dedicated telemetry datasource (OS_TELEMETRY_DB);os db cleanreclaims SQLite space. - Effective-dated grants & delegation (14.4, ADR-0091) — position and
permission-set assignments support
valid_from/valid_untilwindows (fail-closed, no background job), anddelegatablepositions let holders self-delegate for a bounded window (≤ 30 days, reason required, never admin scopes). - MCP scope ceiling (14.5, ADR-0090 D10) — OAuth-connected agents run
under
effective_permission = scope_ceiling ∩ user_grants(data:read/data:write/actions:execute), failing closed. - Security fixes (14.4–14.5) — FLS keys must be object-qualified
(bare keys silently matched nothing; now lint-rejected with autofix);
settings and share-link routes no longer trust spoofable
x-user-id-style headers; analytics queries are scoped to the caller's read filter.
13.x — Permission Model v2
@objectstack 13.0 restructured authorization (ADR-0090). Breaking:
- Roles and Profiles converge on Positions —
sys_role*tables,RoleSchema/defineRole, metadata kindsrole/profile, andExecutionContext.roles[]are gone; positions are flat and hierarchy moved to the business-unit tree. Sharing recipients renamed (role→position,role_and_subordinates→unit_and_subordinates). - Custom objects default to private — an owner-carrying object with
no explicit
sharingModelis now private; theread/read_write/fullOWD aliases were removed. DeclaresharingModel: 'public_read_write'to restore prior behavior. - RBAC link-table writes gated — writes to
sys_user_position,sys_position_permission_set,sys_user_permission_set, andsys_permission_setrequire tenant admin or a delegated-admin scope.
And new: everyone / guest audience anchors, delegated administration
(PermissionSet.adminScope), the explain() engine with per-layer
attribution, the access-matrix snapshot gate for os compile,
self-serve OAuth 2.1 for MCP clients, author-time security lints
(validateSecurityPosture), per-operation Object.requiredPermissions
maps, and package namespace-prefix enforcement. Never-enforced schemas
(compliance / masking / global-RLS configs) were removed outright.
12.x
@objectstack 12.0 hardened the API default posture:
- Anonymous data access denied by default (breaking) —
api.requireAuthnow defaults totrue; anonymous/data/*requests get 401. Public-data deployments must opt out explicitly withapi: { requireAuth: false }(boot warning). Share links, public forms,/auth, and/healthare unaffected. - Enforced protocol handshake —
PROTOCOL_VERSION+checkProtocolCompat()reject incompatible packages at install time. - Adaptive record surfaces — records derive page vs. modal/drawer
display from field complexity;
FormField.spangoes responsive ('auto'/'full'); related lists gain'primary'tab promotion. - Package-shipped permissions — packages declare default permission sets that materialize at boot with provenance tracking.
- Build-time lints —
lint-view-refs,validateListViewMode,validateFormLayout, and destructive-operation RBAC mapping gate compilation.
10.x
The 10.x train shipped on @objectstack 10.0 – 10.2. Every breaking
change landed in the multi-org tenancy layer, so a single-tenant ObjectOS
deployment rolled forward from 9.x with no config changes. What moved:
- Row-level org scoping split into its own plugin (10.0, breaking) — the
organization_idauto-stamp, per-org seed replay, and default-org bootstrap moved out of@objectstack/plugin-securityinto the opt-in@objectstack/plugin-org-scoping. Single-org deployments are leaner (no wildcard RLS to strip on every query), andOS_MULTI_TENANT=truestill auto-registers the plugin ahead ofplugin-security, so projects driven by the CLI need no code changes. Per ADR-0002, a tenant is physical isolation (one Environment = one database);organization_idis logical scoping inside one DB, so it earns its own plugin. - No more automatic personal workspaces (10.0, breaking) —
plugin-securityno longer creates a "<User>'s Workspace" org for every new user. Users now accept an invitation or explicitly create their first organization (the Slack / Linear / GitHub-Orgs model). The first registered user — auto-promoted to platform admin — still gets a singleDefault Organizationin multi-tenant mode so their session resolves RLS; single-tenant deployments create no orgs. recordform field type (ADR-0014) —Record<string, X>properties (such as an object'sfieldsmap) are now editable as first-class form fields in Studio instead of leaking through as raw JSON, and the object preview pane mounts the realObjectGridrenderer — what you preview is what ships.- Pluggable embeddings via
IEmbedder— a new embedder protocol plus@objectstack/embedder-openai; the knowledge / RAG adapters now consumeIEmbedderand were renamed to drop theplugin-prefix. - Public Forms — anonymous Web-to-Lead / Web-to-Case forms served at
/f/:slug, a unified FormPage (public/f/:slug+ internal/forms/:name), atype: 'form'action variant, anddefaultDetailFormonObjectSchema. - Cloud identity split —
os cloud loginis now separate fromos login, the cloud control plane moved to a private repo (a leaner OSS runtime), andobjectstack initagain scaffolds a project that builds and boots.
9.x
The 9.x train ran on @objectstack 9.0 – 9.11 before the 10.0 cutover.
The runtime boot contract is unchanged from 8.0 — createStandaloneStack
still takes the same artifact, environment, and database settings — so an
8.0 deployment rolls forward without config changes. The author-facing
surface is what moved:
- Analytics datasets are the single author surface (9.0, breaking) —
dashboard widgets, reports, and list-charts now bind a semantic
dataset(defineDataset(...)) and select dimensions/measures by name. The old inline query fields (object/valueField/aggregateon widgets,objectName/columns/groupingsDownon reports,xAxisField/yAxisFieldson list-charts) were removed. Migration: move the inline query into adefineDatasetand reference it by name.ChartTypeSchemaalso dropped 8 variant types that only rendered as their base (stacked-bar→bar,spline→line,bubble→scatter, …). - Stricter build-time validation (9.6–9.7) —
os compilenow fails on bare field references (amountinstead ofrecord.amount), unknown CEL functions, and wrong flow-value interpolation syntax, each with a did-you-mean hint. A stack that previously "built but was silently wrong" now fails loudly — re-runos compileafter upgrading and fix what it flags. - Number-field formulas compute mixed arithmetic (9.7) —
record.amount / 100andrecord.price * 2now evaluate instead of silently yieldingnull; the/ 100.0float-literal workaround is no longer needed. - Object-level REST gating, now enforced (ADR-0049) — an object's
apiEnabled: falseremoves it from the REST surface, and anapiMethodswhitelist restricts which operations are reachable. Previously parsed but not enforced. - Package documentation as metadata +
booknavigation (9.3–9.6) —src/docs/*.mdregister asdocmetadata; thebookelement (ADR-0046) declares a derived-membership navigation spine, served atGET /api/v1/meta/book/:name/treewith audience gating. os package install(9.3) — install a package into a running runtime from a catalog id or an inline air-gapped artifact, authenticating with--email/--password.- Approvals (9.3) — send-back-for-revision (
maxRevisions, default 3), jobs-backed SLA auto-escalation, list search/pagination, and session-less bilingual approve/reject confirm links. - Inbound webhook flow triggers (9.3) — a
type: 'api'flow mounts an HMAC-verifiedPOST /api/v1/automation/hooks/:flowName/:hookIdendpoint with idempotent, queue-backed ingestion. - Notification retention defaults on (9.5) — notification history
auto-prunes at 90 days; set the messaging
retentionDays: 0to keep history forever. - CLI bundles AI provider SDKs (9.0) — OpenAI-compatible providers (DeepSeek, DashScope, SiliconFlow, OpenRouter, Cloudflare) work out of the box on a globally-installed CLI.
One flow-authoring behavior change to note: a create_record node's
outputVariable now holds the created record object (was the bare id), so
update {var} references that expected an id to {var.id}.
8.0.x
ObjectOS One and the bundled server shipped on @objectstack 8.0.1.
- MCP over Streamable HTTP — every deployment can act as a
network-reachable Model Context Protocol
server. Opt in with
OS_MCP_SERVER_ENABLED=true; the endpoint is served at/api/v1/mcpwith fail-closed auth (anonymous requests are rejected). The plugin was renamed@objectstack/plugin-mcp-server→@objectstack/mcp. - Self-serve API keys —
POST /api/v1/keysmints a show-oncesys_api_key. The REST data and metadata APIs (/api/v1/data,/api/v1/meta) now authenticate API keys through the same verifier as MCP, running under the key owner's permissions and record-level security. - Field-level conditional rules —
visibleWhen,readonlyWhen, andrequiredWhenare enforced server-side by ObjectQL, not just in the form UI. - Reusable RLS read filter —
security.getReadFilter(object, context)exposes the record-access read scope; analytics datasets, dashboards, and reports bridge to it and fail closed when the scope cannot be applied. - Standalone host stack — the runtime ships a single-tenant
createStandaloneStackhost; the 7.x cloud-connected, hostname-routedcreateObjectOSStackwrapper was removed. A cloud deployment now pointsOS_ARTIFACT_FILEat a published artifact URL.
5.0 — project → environment rename (shipped)
The runtime concept formerly called Project was renamed to Environment throughout. Affected:
- CLI flags:
--environment/-e - HTTP paths:
/api/v1/environments/:environmentId/... - Headers:
X-Environment-Id - Env vars:
OS_ENVIRONMENT_ID(OS_PROJECT_IDkept as a deprecated alias) - DB columns:
environment_id - JSON schemas:
EnvironmentArtifact
Upgrading
See Upgrade and Rollback for the mechanical steps. Pre-flight:
- Read the CHANGELOG entry for every minor between your current and target version.
- Run
os diff <old-artifact> <new-artifact>to surface breaking schema changes. - Run
os doctoragainst the target version. - Bring up one canary instance before rolling the fleet.
- Have a rollback plan for both the image tag and the artifact version (they roll independently).
Reporting regressions
If a patch or minor breaks something previously working, file a bug at github.com/objectstack-ai/objectos/issues with the version you upgraded from / to. We treat regressions as the highest-priority class of bug.