From fcd62c4e3da083955bd8995c749272aa448b8ddc Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 28 Jun 2026 01:06:00 +0800 Subject: [PATCH] docs: align hardening + driver docs with Hono-only adapters (#2392, 12.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the adapter trim (#2391). Hand-written docs only (ADRs / audits / CHANGELOGs left as historical): - docs/HARDENING.md: Fastify recipe → Hono recipe (the @objectstack/fastify import was broken after removal); CSRF → hono/csrf; CORS → createHonoApp. - docs/design/driver-turso.md: drop plugin-msw row; "all adapters" → Hono. - packages/plugins/driver-memory/README.md: drop plugin-msw bullets. - packages/runtime/README.md: framework examples narrowed to Hono. - docs/launch-readiness.md: stale express/fastify residual note → Hono. Verified: no removed-package refs remain in current docs. Co-Authored-By: Claude Opus 4.8 --- .changeset/v12-docs-adapter-accuracy.md | 12 ++++++ docs/HARDENING.md | 52 ++++++++++++++---------- docs/design/driver-turso.md | 3 +- docs/launch-readiness.md | 2 +- packages/plugins/driver-memory/README.md | 2 - packages/runtime/README.md | 6 +-- 6 files changed, 47 insertions(+), 30 deletions(-) create mode 100644 .changeset/v12-docs-adapter-accuracy.md diff --git a/.changeset/v12-docs-adapter-accuracy.md b/.changeset/v12-docs-adapter-accuracy.md new file mode 100644 index 0000000000..14e1281b72 --- /dev/null +++ b/.changeset/v12-docs-adapter-accuracy.md @@ -0,0 +1,12 @@ +--- +"@objectstack/runtime": patch +"@objectstack/driver-memory": patch +--- + +docs: align hardening / driver docs with the Hono-only adapter surface (12.0) + +Follow-up to the adapter trim (#2391): the hardening guide's rate-limit/CORS +recipes are rewritten from Fastify to **Hono** (the shipped adapter; the old +`@objectstack/fastify` import was broken), CSRF guidance points at `hono/csrf`, +and stale `@objectstack/plugin-msw` references are dropped from the driver-memory +and driver-turso docs. README framework lists narrowed to Hono. diff --git a/docs/HARDENING.md b/docs/HARDENING.md index 535a796f2d..89efc56c53 100644 --- a/docs/HARDENING.md +++ b/docs/HARDENING.md @@ -14,7 +14,7 @@ adapter layer for a production deployment. | Security response headers (CSP/XCTO/…) | **On** | `@objectstack/runtime` | | HSTS | Off (opt-in) | `securityHeaders.hsts: true` | | Token-bucket rate limit | Off (opt-in) | `RateLimiter` primitive | -| CSRF | Adapter-layer concern | helmet / @fastify/csrf-protection | +| CSRF | Adapter-layer concern | `hono/secure-headers` / `hono/csrf` | | Auth (better-auth) | On | `@objectstack/plugin-auth` | | Project membership (RBAC) | On when scoped | dispatcher plugin | | Field- and row-level perms | On | SecurityPlugin | @@ -71,16 +71,16 @@ const write = new RateLimiter(DEFAULT_RATE_LIMITS.write); // 60 req / min / IP const read = new RateLimiter(DEFAULT_RATE_LIMITS.read); // 600 req / min / IP ``` -### Fastify recipe +### Hono recipe ```ts -import Fastify from 'fastify'; -import { objectStackPlugin } from '@objectstack/fastify'; +import { Hono } from 'hono'; +import { secureHeaders } from 'hono/secure-headers'; +import { objectStackMiddleware } from '@objectstack/hono'; import { RateLimiter, DEFAULT_RATE_LIMITS } from '@objectstack/runtime'; -import helmet from '@fastify/helmet'; -const app = Fastify({ trustProxy: 1 }); // trust ONE upstream proxy hop -await app.register(helmet, { contentSecurityPolicy: false }); +const app = new Hono(); +app.use('*', secureHeaders()); // CSP / X-Content-Type-Options / X-Frame-Options / … const buckets = { auth: new RateLimiter(DEFAULT_RATE_LIMITS.auth), @@ -88,20 +88,23 @@ const buckets = { read: new RateLimiter(DEFAULT_RATE_LIMITS.read), }; -app.addHook('onRequest', async (req, reply) => { - const ip = req.ip; - const bucket = req.url.startsWith('/api/v1/auth/') ? 'auth' - : ['POST','PUT','PATCH','DELETE'].includes(req.method) ? 'write' +// Rate-limit BEFORE the dispatcher middleware so rejected requests never reach it. +app.use('/api/v1/*', async (c, next) => { + const ip = c.req.header('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown'; + const bucket = c.req.path.startsWith('/api/v1/auth/') ? 'auth' + : ['POST','PUT','PATCH','DELETE'].includes(c.req.method) ? 'write' : 'read'; const decision = buckets[bucket].consume(`${ip}:${bucket}`); - reply.header('X-RateLimit-Remaining', String(decision.remaining)); + c.header('X-RateLimit-Remaining', String(decision.remaining)); if (!decision.allowed) { - reply.header('Retry-After', String(Math.ceil(decision.retryAfterMs / 1000))); - return reply.code(429).send({ error: 'Too many requests' }); + c.header('Retry-After', String(Math.ceil(decision.retryAfterMs / 1000))); + return c.json({ error: 'Too many requests' }, 429); } + await next(); }); -app.register(objectStackPlugin, { kernel, prefix: '/api/v1' }); +app.use('/api/v1/*', objectStackMiddleware(kernel)); // dispatcher last +export default app; // Cloudflare Workers / Bun / Deno / Node (@hono/node-server) ``` For multi-instance deploys, replace the default `MemoryStore` with a @@ -131,8 +134,8 @@ Bearer …`. With bearer tokens stored in `localStorage` / memory there is no CSRF surface — browsers don't auto-attach the header. CSRF protection becomes required when you switch to **cookie-based -session auth**. In that case wire `@fastify/csrf-protection` (or the -equivalent for your adapter) and exempt only the auth callback routes. +session auth**. In that case wire a CSRF middleware (e.g. `hono/csrf`) +and exempt only the auth callback routes. ## JWT / session lifecycle @@ -167,13 +170,18 @@ curl -H "Authorization: Bearer $TOK" $API/data/account CORS is intentionally **not** opinionated by the runtime — it's an app-level policy that depends on which origins host your front-end. -Configure at the adapter: +Configure it on the Hono adapter: ```ts -import cors from '@fastify/cors'; -await app.register(cors, { - origin: ['https://app.example.com'], - credentials: true, +import { createHonoApp } from '@objectstack/hono'; + +const app = createHonoApp({ + kernel, + prefix: '/api/v1', + cors: { + origin: ['https://app.example.com'], + credentials: true, + }, }); ``` diff --git a/docs/design/driver-turso.md b/docs/design/driver-turso.md index f698072a7c..9d3c6647b3 100644 --- a/docs/design/driver-turso.md +++ b/docs/design/driver-turso.md @@ -90,7 +90,7 @@ offline-capable desktop applications. | `@objectstack/rest` | 🟢 None | REST API is driver-agnostic | | `@objectstack/metadata` | 🟢 None | Metadata service is storage-agnostic | | `@objectstack/cli` | 🟡 Minor | Add `driver-turso` to `create-objectstack` templates | -| Framework Adapters | 🟢 None | All adapters (Next.js, NestJS, Hono, etc.) are driver-agnostic | +| Framework Adapters | 🟢 None | The Hono adapter is driver-agnostic | **Key Insight:** The microkernel architecture means adding a new driver has **zero impact** on the server-side stack. The `IDataDriver` contract completely decouples the data layer. @@ -122,7 +122,6 @@ export default defineStack({ |:---|:---:|:---| | `@objectstack/client` | 🟢 None | Client SDK communicates via REST/GraphQL; driver-agnostic | | `@objectstack/client-react` | 🟢 None | React hooks use client SDK; no changes | -| `@objectstack/plugin-msw` | 🟢 None | MSW mocks REST endpoints; driver-irrelevant | **New Capability Unlocked:** With embedded replicas, a future `@objectstack/client-local` package could provide direct libSQL access in the browser (via WASM), enabling: diff --git a/docs/launch-readiness.md b/docs/launch-readiness.md index 5befa5a050..2f574f46c5 100644 --- a/docs/launch-readiness.md +++ b/docs/launch-readiness.md @@ -175,7 +175,7 @@ fix or acceptance.** draining them. Replaced with: `server.close()` (stop new + drain active) + `closeIdleConnections()` (release idle keep-alive), and force-close only after a bounded **drain window** (default 10s, < the kernel's 60s). +2 integration tests. -- **Residual (not blocking):** embedding framework adapters (express/fastify/…) +- **Residual (not blocking):** the Hono adapter intentionally leave signal handling to the host app; cluster/Redis close should be registered via `kernel.onShutdown(...)` by the cluster plugin — confirm it is. - **Owner:** _______ · Verify ✅ (mostly false positive; drain bug fixed) · Sign-off ☐ · Notes: Kernel shutdown already correct; hono drain fixed + tested. Awaiting human sign-off. diff --git a/packages/plugins/driver-memory/README.md b/packages/plugins/driver-memory/README.md index 0805564a37..552c1e93ca 100644 --- a/packages/plugins/driver-memory/README.md +++ b/packages/plugins/driver-memory/README.md @@ -79,7 +79,6 @@ const driver = new InMemoryDriver({ ## When to use - ✅ Development, unit tests, CI, Storybook. -- ✅ Browser-only demos pairing with [`@objectstack/plugin-msw`](../plugin-msw). ## When not to use @@ -90,7 +89,6 @@ const driver = new InMemoryDriver({ - [`@objectstack/objectql`](../../objectql) — query engine. - [`@objectstack/driver-sql`](../driver-sql) — production driver (ObjectStack Cloud ships an additional `@objectstack/driver-turso` for edge/multi-tenant deployments). -- [`@objectstack/plugin-msw`](../plugin-msw) — browser mock API. ## Links diff --git a/packages/runtime/README.md b/packages/runtime/README.md index 17b997e7ba..7550f91469 100644 --- a/packages/runtime/README.md +++ b/packages/runtime/README.md @@ -134,7 +134,7 @@ new AppPlugin(appConfig) #### IHttpServer -Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (Express, Fastify, Hono, etc.) without tight coupling. +Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (e.g. Hono) without tight coupling. ```typescript import { IHttpServer, IHttpRequest, IHttpResponse } from '@objectstack/runtime'; @@ -144,7 +144,7 @@ class MyHttpServerPlugin implements Plugin { name = 'http-server'; async init(ctx: PluginContext) { - const server: IHttpServer = createMyServer(); // Express, Hono, etc. + const server: IHttpServer = createMyServer(); // Hono, or any framework via IHttpServer ctx.registerService('http-server', server); } } @@ -599,7 +599,7 @@ createDispatcherPlugin({ Token-bucket `RateLimiter` with pluggable `RateLimitStore` (in-memory default, Redis-friendly contract). Curated `DEFAULT_RATE_LIMITS` for auth / write / read -buckets. Fastify / Hono / Express recipes in +buckets. A Hono recipe in [`docs/HARDENING.md`](../../docs/HARDENING.md#rate-limiting). ```ts