Add a single platform-owned request-body policy (hono/body-limit) with small, named limits for Better Auth and Core's grant-mutation endpoint, and a new required maxBodyBytes field on the executable module route contract so every module route makes a conscious no-body-or-bounded-body choice, capped at a platform ceiling. Every oversized request gets one sanitized 413 contract and never reaches business logic, authorization, or a handler. Also configure finite Node header/request/keep-alive timeouts, including the non-obvious connectionsCheckingInterval needed for them to take effect promptly, so a slow-header or slow-body connection cannot hold a socket indefinitely. Co-Authored-By: Claude Sonnet 5 <[email protected]>
39 lines
1.7 KiB
TypeScript
39 lines
1.7 KiB
TypeScript
import { defineModuleRuntime } from "@ekklesia/module-sdk";
|
|
import { records } from "../schema/tables.js";
|
|
|
|
export const recordsCapability = Object.freeze({
|
|
kind: "example-records",
|
|
});
|
|
|
|
export default defineModuleRuntime({
|
|
id: "example",
|
|
version: "0.1.0",
|
|
register({ capabilities, http }) {
|
|
capabilities.provide("example.records", recordsCapability);
|
|
|
|
// The accepted module database boundary: `context.services.database` — provided only to an
|
|
// authorized handler, never to `register` — is this module's one way to reach PostgreSQL. It
|
|
// is a fully typed Drizzle query surface over the module's own table (`../schema/tables.js`),
|
|
// never a raw `pg` client, a connection string, or a Drizzle instance this module built
|
|
// itself. `example.records.read` is declared in `ekklesia.module.json`, exactly like every
|
|
// other permission a module route enforces.
|
|
http.route({
|
|
method: "GET",
|
|
path: "/records",
|
|
permission: "example.records.read",
|
|
// This route accepts no request body at all — a GET route never should. Every route must
|
|
// make this choice consciously: 0 means "no body accepted" (a non-empty declared or streamed
|
|
// body is rejected before this handler ever runs); a route that does accept a body declares
|
|
// its own explicit byte maximum instead, up to the platform's ceiling
|
|
// (`MODULE_ROUTE_MAX_BODY_BYTES_CEILING`, `@ekklesia/contracts`).
|
|
maxBodyBytes: 0,
|
|
async handler(_request, context) {
|
|
const rows = await context.services.database
|
|
.select({ id: records.id, value: records.value })
|
|
.from(records);
|
|
return Response.json(rows);
|
|
},
|
|
});
|
|
},
|
|
});
|