Files
suaveplan/packages/api-stack/api/api-manifest/src/schema/permissions.md
2026-05-27 23:29:53 +01:00

49 lines
2.9 KiB
Markdown

# `permissionsSchema` + `budgetsSchema`
## Purpose
Zod schemas for the sandbox permissions block. `permissions.fs` and `permissions.fetch` are host-proxied allowlists honoured only when isolation is `sandboxed`; `permissions.budgets` carries CPU/memory/wall-clock caps for the per-request quota enforcer.
## Features
- Optional `fs` and `fetch` allowlists (readonly string arrays)
- `budgets.cpuPercent` clamped to 1..100
- `budgets.memoryMb` and `budgets.wallClockMs` positive integers
- Cross-field rule `validatePermissionsWhenSandboxed` enforces budgets-when-sandboxed at a separate layer
## Basic Example
```ts
import { permissionsSchema } from "@suaveplan/api-manifest/schema";
permissionsSchema.parse({ budgets: { cpuPercent: 25, memoryMb: 512 } });
```
## Advanced Example
```ts
import { permissionsSchema, budgetsSchema } from "@suaveplan/api-manifest/schema";
const budgets = budgetsSchema.parse({ cpuPercent: 25, memoryMb: 512, wallClockMs: 30000 });
const perms = permissionsSchema.parse({
fs: ["./uploads/*"],
fetch: ["api.stripe.com"],
budgets,
});
```
## API Reference
- `permissionsSchema: z.ZodObject<{ fs?, fetch?, budgets? }>`
- `budgetsSchema: z.ZodObject<{ cpuPercent?, memoryMb?, wallClockMs? }>`
- `Permissions`, `Budgets` — inferred type aliases.
## Implementation Notes
The `fs` and `fetch` allowlists use plain strings rather than parsed globs/URLs to keep the schema decoupled from any specific matcher (`micromatch`, `globby`, etc.); the host plugin that enforces the sandbox chooses its own matcher. `cpuPercent` is capped at 100 so a misconfigured plugin can't request more than a full core. Every field is optional at the schema level; the `sandboxed`-tier requirement of having budgets at all is enforced at the cross-field validator layer.
## Cross-package Notes
This module is part of the `@suaveplan/api-manifest` Stage-1 contract package. It sits beneath every Stage-2 consumer (`api-host`, `api-loader`, the admin CLI, the IDE plugin) and above the foundation packages (`@suaveplan/error`, `@suaveplan/types`). The strict-mode top-level rejection of unknown keys means upgrading from manifest v1 to v2 is an OpenSpec-driven event: a new key landing in a consumer manifest is a parse failure rather than a silent drift, which is exactly the contract this stage exists to guarantee.
When the host reloads a plugin without a full restart, every manifest field this module validates is compared against the install-time `manifestHash`. A mismatch triggers a hard rollback via the `RollbackJournal` because the host's invariants (route mounts, registered capabilities, declared scopes) are derived from these exact bytes. Treat the schemas and validators here as the contract surface, not just an input filter — downstream code reads `manifest.foo.bar` and relies on those keys being present, well-typed, and non-undefined in the documented optionality regions.