diff --git a/.claude/workflows/docs-accuracy-audit.js b/.claude/workflows/docs-accuracy-audit.js
index d8b47af209..6a85f6c3f0 100644
--- a/.claude/workflows/docs-accuracy-audit.js
+++ b/.claude/workflows/docs-accuracy-audit.js
@@ -98,7 +98,6 @@ const ALL_HANDWRITTEN = [
"content/docs/deployment/cli.mdx",
"content/docs/deployment/environment-variables.mdx",
"content/docs/deployment/index.mdx",
- "content/docs/deployment/migration-from-objectql.mdx",
"content/docs/deployment/production-readiness.mdx",
"content/docs/deployment/publish-and-preview.mdx",
"content/docs/deployment/self-hosting.mdx",
@@ -106,7 +105,6 @@ const ALL_HANDWRITTEN = [
"content/docs/deployment/tenancy-modes.mdx",
"content/docs/deployment/troubleshooting.mdx",
"content/docs/deployment/validating-metadata.mdx",
- "content/docs/deployment/vercel.mdx",
"content/docs/getting-started/build-with-claude-code.mdx",
"content/docs/getting-started/common-patterns.mdx",
"content/docs/getting-started/examples.mdx",
diff --git a/apps/docs/redirects.mjs b/apps/docs/redirects.mjs
index 2f5ccb8e40..f853bb6d5d 100644
--- a/apps/docs/redirects.mjs
+++ b/apps/docs/redirects.mjs
@@ -92,14 +92,23 @@ export const docsRedirects = [
['/docs/concepts/cluster-semantics', '/docs/kernel/cluster'],
// deployment
['/docs/guides/cloud-deployment', '/docs/deployment'],
- ['/docs/guides/deployment-vercel', '/docs/deployment/vercel'],
+ ['/docs/guides/deployment-vercel', '/docs/deployment/self-hosting'],
['/docs/guides/production-readiness', '/docs/deployment/production-readiness'],
['/docs/guides/publish-and-preview', '/docs/deployment/publish-and-preview'],
['/docs/guides/environment-variables', '/docs/deployment/environment-variables'],
['/docs/guides/single-project-mode', '/docs/deployment/single-project-mode'],
['/docs/concepts/cloud-artifact-api', '/docs/deployment/cloud-artifact-api'],
- ['/docs/guides/objectql-migration', '/docs/deployment/migration-from-objectql'],
+ ['/docs/guides/objectql-migration', '/docs/deployment'],
['/docs/guides/troubleshooting', '/docs/deployment/troubleshooting'],
+ // Retired deployment pages (maintainer ruling 2026-08-15). Docker /
+ // self-hosting is the shipping path, so the Vercel guide lands on
+ // self-hosting; the `@objectql/core` port guide has no successor page, so it
+ // lands on the section index. The two `/docs/guides/*` entries ABOVE are
+ // re-pointed at these same destinations rather than left aimed here: Next
+ // matches this table once per request, so a chained entry would cost the old
+ // URLs a second round trip (and every redirect hop is SEO signal lost).
+ ['/docs/deployment/vercel', '/docs/deployment/self-hosting'],
+ ['/docs/deployment/migration-from-objectql', '/docs/deployment'],
// protocol / releases
['/docs/guides/cheatsheets/protocol-diagram', '/docs/protocol/diagram'],
['/docs/guides/cheatsheets/backward-compatibility', '/docs/protocol/backward-compatibility'],
diff --git a/content/docs/deployment/meta.json b/content/docs/deployment/meta.json
index 84498c36e3..aef1433b2e 100644
--- a/content/docs/deployment/meta.json
+++ b/content/docs/deployment/meta.json
@@ -6,13 +6,11 @@
"validating-metadata",
"self-hosting",
"backup-restore",
- "vercel",
"production-readiness",
"publish-and-preview",
"environment-variables",
"single-project-mode",
"tenancy-modes",
- "migration-from-objectql",
"troubleshooting"
]
}
diff --git a/content/docs/deployment/migration-from-objectql.mdx b/content/docs/deployment/migration-from-objectql.mdx
deleted file mode 100644
index 980708f473..0000000000
--- a/content/docs/deployment/migration-from-objectql.mdx
+++ /dev/null
@@ -1,179 +0,0 @@
----
-title: Migrating from @objectql/core
-description: Step-by-step guide for migrating from @objectql/core to @objectstack/objectql
----
-
-# Migrating from `@objectql/core` to `@objectstack/objectql`
-
-The core functionality previously provided by `@objectql/core` — the introspection types, the utility functions, and the kernel factory — was upstreamed into `@objectstack/objectql` in **v3.0.4** and has shipped in every release since (the current release line is **v16.x**). This guide walks you through migrating your project so that `@objectql/core` can be removed.
-
-## Why Migrate?
-
-| | `@objectql/core` (deprecated) | `@objectstack/objectql` (recommended) |
-| :--- | :--- | :--- |
-| **Engine** | Bridge class extending upstream `ObjectQL` | Canonical `ObjectQL` engine |
-| **Kernel Factory** | `createObjectQLKernel()` | `createObjectQLKernel()` (identical API) |
-| **Introspection** | `toTitleCase`, `convertIntrospectedSchemaToObjects` | Same functions, same signatures |
-| **Registry** | Re-exports from upstream | Direct exports |
-| **MetadataRegistry** | `@objectql/types` `MetadataRegistry` class | `MetadataFacade` (async `IMetadataService`) |
-| **Maintenance** | Planned deprecation | Actively maintained |
-
-## Step 1 — Update Dependencies
-
-```diff
- // package.json
- {
- "dependencies": {
-- "@objectql/core": "^4.x",
-- "@objectql/types": "^4.x",
-+ "@objectstack/objectql": "^16.1.0"
- }
- }
-```
-
-Then re-install:
-
-```bash
-pnpm install
-```
-
-## Step 2 — Update Imports
-
-### Engine, Repository & Context
-
-```diff
-- import { ObjectQL, ObjectRepository, ScopedContext } from '@objectql/core';
-+ import { ObjectQL, ObjectRepository, ScopedContext } from '@objectstack/objectql';
-```
-
-```diff
-- import type { HookHandler, HookEntry, OperationContext, EngineMiddleware } from '@objectql/core';
-+ import type { HookHandler, HookEntry, OperationContext, EngineMiddleware } from '@objectstack/objectql';
-```
-
-### Schema Registry
-
-```diff
-- import { SchemaRegistry } from '@objectql/core';
-+ import { SchemaRegistry } from '@objectstack/objectql';
-```
-
-### Kernel Factory
-
-```diff
-- import { createObjectQLKernel } from '@objectql/core';
-- import type { ObjectQLKernelOptions } from '@objectql/core';
-+ import { createObjectQLKernel } from '@objectstack/objectql';
-+ import type { ObjectQLKernelOptions } from '@objectstack/objectql';
-```
-
-### Utility Functions
-
-```diff
-- import { toTitleCase, convertIntrospectedSchemaToObjects } from '@objectql/core';
-+ import { toTitleCase, convertIntrospectedSchemaToObjects } from '@objectstack/objectql';
-```
-
-### Introspection Types
-
-```diff
-- import type { IntrospectedSchema, IntrospectedTable } from '@objectql/types';
-+ import type { IntrospectedSchema, IntrospectedTable } from '@objectstack/objectql';
-```
-
-```diff
-- import type { IntrospectedColumn, IntrospectedForeignKey } from '@objectql/types';
-+ import type { IntrospectedColumn, IntrospectedForeignKey } from '@objectstack/objectql';
-```
-
-## Step 3 — API Differences
-
-### `createObjectQLKernel()`
-
-The function signature is identical. The only change is the return type uses the upstream `ObjectKernel`:
-
-```typescript
-import { createObjectQLKernel } from '@objectstack/objectql';
-
-const kernel = await createObjectQLKernel({
- plugins: [myDriverPlugin],
-});
-await kernel.bootstrap();
-```
-
-### `convertIntrospectedSchemaToObjects()`
-
-Same signature and behavior:
-
-```typescript
-import { convertIntrospectedSchemaToObjects } from '@objectstack/objectql';
-
-const objects = convertIntrospectedSchemaToObjects(schema, {
- excludeTables: ['migrations'],
- includeTables: undefined, // optional: whitelist
- skipSystemColumns: true, // default: skips id, created_at, updated_at
-});
-```
-
-### MetadataRegistry → MetadataFacade
-
-If you were using `MetadataRegistry` from `@objectql/types`, the upstream equivalent is `MetadataFacade`. There are two differences: `MetadataFacade` is constructed over a `SchemaRegistry` instance, and its methods are **async** (returns `Promise`):
-
-```diff
-- import { MetadataRegistry } from '@objectql/types';
-- const registry = new MetadataRegistry();
-- registry.register('object', myObject);
-- const obj = registry.get('object', 'account');
-+ import { MetadataFacade, SchemaRegistry } from '@objectstack/objectql';
-+ const facade = new MetadataFacade(new SchemaRegistry());
-+ await facade.register('object', 'account', myObject);
-+ const obj = await facade.get('object', 'account');
-```
-
-### ObjectQLPlugin
-
-Both packages export an `ObjectQLPlugin`, but they serve different roles:
-
-| | `@objectql/core` `ObjectQLPlugin` | `@objectstack/objectql` `ObjectQLPlugin` |
-| :--- | :--- | :--- |
-| Interface | `RuntimePlugin` (from `@objectql/types`) | `Plugin` (from `@objectstack/core`) |
-| Role | Orchestrator composing sub-plugins (validator, formula, query) | Kernel plugin registering ObjectQL as core services |
-| Methods | `install()`, `onStart()`, `init()`, `start()` | `init()`, `start()` |
-
-If you were using the `@objectql/core` `ObjectQLPlugin` to compose sub-plugins (`ValidatorPlugin`, `FormulaPlugin`, `QueryPlugin`), those downstream plugins are **not** part of this migration — they remain in the `@objectql` ecosystem.
-
-## Step 4 — Type Mapping Reference
-
-The `convertIntrospectedSchemaToObjects()` utility maps database column types as follows:
-
-| Database Type | ObjectStack FieldType |
-| :--- | :--- |
-| `varchar`, `char` | `text` |
-| `text` | `textarea` |
-| `integer`, `int`, `bigint`, `smallint` | `number` |
-| `float`, `double`, `decimal`, `numeric`, `real` | `number` |
-| `boolean`, `bool` | `boolean` |
-| `timestamp`, `datetime` | `datetime` |
-| `date` | `date` |
-| `time` | `time` |
-| `json`, `jsonb` | `json` |
-| Foreign key column | `lookup` (with `reference` set to target table) |
-| _anything else_ | `text` (fallback) |
-
-## Step 5 — Remove `@objectql/core`
-
-Once all imports are updated and tests pass:
-
-```bash
-pnpm remove @objectql/core @objectql/types
-```
-
-## Checklist
-
-- [ ] Replace `@objectql/core` dependency with `@objectstack/objectql` in `package.json`
-- [ ] Update all `import` statements (engine, registry, kernel factory, utilities, types)
-- [ ] Replace `MetadataRegistry` usage with `MetadataFacade` (add `await`)
-- [ ] Verify `ObjectQLPlugin` usage matches the upstream interface
-- [ ] Run `pnpm build` and fix any remaining type errors
-- [ ] Run `pnpm test` to verify behavior
-- [ ] Remove `@objectql/core` and `@objectql/types` from dependencies
diff --git a/content/docs/deployment/self-hosting.mdx b/content/docs/deployment/self-hosting.mdx
index 0a9898d88c..39a2684da7 100644
--- a/content/docs/deployment/self-hosting.mdx
+++ b/content/docs/deployment/self-hosting.mdx
@@ -7,8 +7,7 @@ description: Run a compiled ObjectStack app on your own infrastructure — bare
This guide takes the artifact produced by `os build` / `os compile` and runs it
on infrastructure **you** operate: a Linux host, a Docker container, or a
-compose stack with Postgres. It complements the platform-specific
-[Vercel guide](/docs/deployment/vercel) and assumes you have read
+compose stack with Postgres. It assumes you have read
[Deployment Modes](/docs/deployment).
The deployment model is deliberately simple:
@@ -421,5 +420,4 @@ Disable with `OS_MCP_SERVER_ENABLED=false`. See
- [Deployment Modes](/docs/deployment) — the map of local / standalone / Cloud
- [`os start` reference](/docs/deployment/cli#os-start) — every flag and env var
- [Environment Variables](/docs/deployment/environment-variables) — the full catalog
-- [Deploy to Vercel](/docs/deployment/vercel) — the serverless alternative
- [Troubleshooting & FAQ](/docs/deployment/troubleshooting)
diff --git a/content/docs/deployment/vercel.mdx b/content/docs/deployment/vercel.mdx
deleted file mode 100644
index ed2e051ed7..0000000000
--- a/content/docs/deployment/vercel.mdx
+++ /dev/null
@@ -1,195 +0,0 @@
----
-title: Deploy to Vercel
-description: Deploy ObjectStack applications to Vercel with the Hono server adapter.
----
-
-# Deploy to Vercel
-
-ObjectStack deploys to Vercel in **server mode** — serverless functions running the **Hono** adapter (`@objectstack/hono`). The Console SPA can also be deployed on its own as a static Vite build that points at a separate ObjectStack server (via `VITE_SERVER_URL`) — but that build comes from the Console source repo ([objectui](https://github.com/objectstack-ai/objectui)), not from the prebuilt `@objectstack/console` package.
-
-
-**Removed in 11.** The in-browser MSW mode (`@objectstack/plugin-msw`) and the Next.js adapter (`@objectstack/nextjs`) are no longer published — use the Hono server path below.
-
-
----
-
-## Server Mode (Serverless Functions)
-
-In Server mode, ObjectStack runs inside Vercel Serverless Functions. API requests are handled by real backend logic with a real database.
-
-### Architecture
-
-```
-┌─── Vercel ────────────────────────────────────────────────────┐
-│ │
-│ Static Assets (React SPA) │
-│ ┌───────────────────────────────────┐ │
-│ │ /index.html, /assets/* │ │
-│ └───────────────────────────────────┘ │
-│ │
-│ Serverless Functions │
-│ ┌───────────────────────────────────┐ │
-│ │ /api/index.ts │ │
-│ │ → ObjectKernel │ │
-│ │ → ObjectQL │ │
-│ │ → PostgreSQL / MongoDB / ... │──── External DB │
-│ └───────────────────────────────────┘ │
-│ │
-└───────────────────────────────────────────────────────────────┘
-```
-
-### Hono serverless function (`@objectstack/hono`)
-
-This is a valid self-contained pattern when you want to ship a Vite SPA *and* its API from a single Vercel project: the SPA is served as static assets, and a Hono-based serverless function handles `/api/*` requests.
-
-
-The framework-vendored Console (`@objectstack/console`) does **not** use this topology. It is a **prebuilt, dist-only** package — no source and no build step, just static assets — that an ObjectStack server mounts at `/_console` (`os serve --ui`, on by default). Its bundle is baked same-origin, and `VITE_SERVER_URL` is a build-time Vite flag, so aiming a Console at a *different* origin means building it yourself from the Console source repo ([objectui](https://github.com/objectstack-ai/objectui)). For the server itself, prefer the CLI (`os serve`) over a hand-rolled Hono function.
-
-
-**1. Create the kernel singleton** (`api/_kernel.ts` — prefixed with `_` to prevent Vercel from creating a route):
-
-```typescript
-// api/_kernel.ts
-import { ObjectKernel, DriverPlugin, AppPlugin } from '@objectstack/runtime';
-import { ObjectQLPlugin } from '@objectstack/objectql';
-import { InMemoryDriver } from '@objectstack/driver-memory';
-import { createHonoApp } from '@objectstack/hono';
-import { Hono } from 'hono';
-import appConfig from '../objectstack.config';
-
-let _kernel: ObjectKernel | null = null;
-let _app: Hono | null = null;
-
-async function ensureKernel(): Promise {
- if (_kernel) return _kernel;
- _kernel = new ObjectKernel();
- await _kernel.use(new ObjectQLPlugin());
- await _kernel.use(new DriverPlugin(new InMemoryDriver(), 'memory'));
- await _kernel.use(new AppPlugin(appConfig));
- await _kernel.bootstrap();
- return _kernel;
-}
-
-export async function ensureApp(): Promise {
- if (_app) return _app;
- const kernel = await ensureKernel();
- _app = createHonoApp({ kernel, prefix: '/api/v1' });
- return _app;
-}
-```
-
-
-`InMemoryDriver` keeps this snippet self-contained, but on serverless **all data
-is lost on every cold start** — fine for a demo, wrong for anything real. Before
-going past a proof of concept, swap the driver for a real database (see
-[Database Drivers](/docs/data-modeling/drivers)). A hand-built kernel does **not**
-resolve `OS_DATABASE_URL` on its own — that precedence lives in the CLI and in
-`createStandaloneStack()` — so read the connection string yourself, e.g.
-`new SqlDriver({ client: 'pg', connection: process.env.OS_DATABASE_URL })`.
-
-
-**2. Create the API entrypoint** (`api/index.ts`):
-
-```typescript
-// api/index.ts
-import { Hono } from 'hono';
-import { handle } from 'hono/vercel';
-import { ensureApp } from './_kernel';
-
-const app = new Hono();
-
-app.all('*', async (c) => {
- const inner = await ensureApp();
- return inner.fetch(c.req.raw);
-});
-
-export default handle(app);
-```
-
-**3. `vercel.json`:**
-
-```json
-{
- "$schema": "https://openapi.vercel.sh/vercel.json",
- "framework": "vite",
- "buildCommand": "vite build",
- "outputDirectory": "dist",
- "build": {
- "env": {
- "VITE_USE_MOCK_SERVER": "false",
- "VITE_SERVER_URL": ""
- }
- },
- "rewrites": [
- { "source": "/api/(.*)", "destination": "/api" },
- { "source": "/((?!api/).*)", "destination": "/index.html" }
- ]
-}
-```
-
-
-Setting `VITE_SERVER_URL` to empty string tells the client SDK to use same-origin API calls. The first rewrite routes all `/api/*` sub-paths to the `api/index.ts` serverless function. The second rewrite excludes `/api/` paths so all other paths serve the SPA.
-
-
----
-
-## Environment Variables
-
-Configure these in Vercel Project Settings → Environment Variables:
-
-### Console / SPA build
-
-| Variable | Description |
-| :--- | :--- |
-| `VITE_USE_MOCK_SERVER` | Leave `false` — the SPA talks to a real backend at `VITE_SERVER_URL`. (The `true` in-browser mock mode was removed in 11.) A **build-time** flag baked into the SPA bundle. |
-| `VITE_SERVER_URL` | Backend API URL (empty for same-origin) |
-
-### Cloud control plane
-
-The Cloud control-plane distribution lives outside this framework repo. If you
-deploy a Cloud host on Vercel, remember that Vercel functions have an
-**ephemeral, read-only filesystem** — local SQLite and local-FS storage will not
-persist between invocations. Use a durable database and S3-compatible object
-storage for artifacts.
-
-| Variable | Required | Description |
-| :--- | :--- | :--- |
-| `OS_AUTH_SECRET` | yes | ≥ 32 chars (`AUTH_SECRET` is accepted as a deprecated alias) |
-| `OS_CONTROL_DATABASE_URL` | yes | `libsql://…` (Turso) — control plane DB |
-| `OS_CONTROL_DATABASE_AUTH_TOKEN` | yes | Turso token (or `TURSO_AUTH_TOKEN`) |
-| `OS_STORAGE_ADAPTER` | yes | `s3` (must, on Vercel) |
-| `OS_S3_BUCKET` | yes (s3) | Bucket name |
-| `OS_S3_REGION` | yes (s3) | `us-east-1`, or `auto` for Cloudflare R2 |
-| `OS_S3_ENDPOINT` | optional | For R2 / MinIO / B2 / non-AWS |
-| `OS_S3_ACCESS_KEY_ID` | optional | Falls back to AWS SDK credential chain |
-| `OS_S3_SECRET_ACCESS_KEY` | optional | Falls back to AWS SDK credential chain |
-| `OS_S3_FORCE_PATH_STYLE` | optional | `1` for MinIO / self-hosted |
-
-> The Cloud control plane's storage backend (S3, Cloudflare R2, MinIO, etc.) is
-> configured and documented in that separate distribution, not in this framework
-> repo. See [Publish, Versioning & Preview](/docs/deployment/publish-and-preview)
-> for the package publish/install workflow the control plane exposes to the CLI.
-
-
-**Never commit secrets.** Use Vercel's environment variable UI or the Vercel CLI (`vercel env add`) to configure tokens and S3 credentials.
-
-
----
-
-## Deployment Checklist
-
-- [ ] `api/index.ts` Hono entrypoint exists with `handle(app)` export
-- [ ] `api/_kernel.ts` boots the kernel with the correct driver
-- [ ] `vercel.json` sets `VITE_USE_MOCK_SERVER=false` and `VITE_SERVER_URL=` (empty)
-- [ ] Rewrite rule routes `/api/*` to `/api` and excludes `/api/` from SPA rewrite
-- [ ] The production connection string is set in Vercel environment variables **and** read explicitly by the driver you construct in `api/_kernel.ts` (`OS_DATABASE_URL` is resolved by the CLI / `createStandaloneStack()`, not by a hand-built kernel)
-- [ ] CORS is configured if frontend and API are on different origins
-
----
-
-## Related
-
-- [Plugin System](/docs/plugins) — the Hono server adapter
-- [Client SDK](/docs/api/client-sdk) — Frontend data fetching
-- [Driver Configuration](/docs/data-modeling/drivers) — Database setup
-- [Architecture](/docs/concepts/architecture) — Kernel and runtime overview
diff --git a/packages/objectql/README.md b/packages/objectql/README.md
index b177ea2334..5b5ebb3b3b 100644
--- a/packages/objectql/README.md
+++ b/packages/objectql/README.md
@@ -96,9 +96,8 @@ computeFQN('base', 'user'); // => 'user' (reserved namespace)
## Migrating from `@objectql/core`
-If you are migrating from the downstream `@objectql/core` package, see the
-[Migration Guide](../../content/docs/deployment/migration-from-objectql.mdx) for
-step-by-step instructions.
+If you are migrating from the downstream `@objectql/core` package, the change is
+an import rewrite — the exports below keep the same names and signatures.
**Quick summary:**
diff --git a/scripts/role-word-baseline.json b/scripts/role-word-baseline.json
index b61b7f14d6..119885f46c 100644
--- a/scripts/role-word-baseline.json
+++ b/scripts/role-word-baseline.json
@@ -7,7 +7,6 @@
"content/docs/concepts/architecture.mdx": 4,
"content/docs/data-modeling/seed-data.mdx": 2,
"content/docs/deployment/cli.mdx": 1,
- "content/docs/deployment/migration-from-objectql.mdx": 2,
"content/docs/getting-started/common-patterns.mdx": 1,
"content/docs/getting-started/index.mdx": 1,
"content/docs/getting-started/quick-start.mdx": 1,