// Backend for the {{projectName}} public status page. Read by `voltro dev`. // // The shape of a status page is asymmetric, and this config encodes it: // • READS are PUBLIC — `incidents.live` / `updates.list` / `components.list` // each declare `openAccess: ''`, so an anonymous visitor's browser can // subscribe and see the live timeline. (A status page that requires a login // is useless during an outage.) // • WRITES are OPERATOR-ONLY — every mutation declares a `status:write` guard. // // Both halves are DECISIONS, and the framework insists on one or the other: // under `security.defaultDeny` a wire-exposed procedure declaring neither does // not boot. `openAccess` is not the lax option — it is the honest spelling of an // open endpoint, and it leaves the reason where a reviewer will read it. // `rbacPlugin` resolves each caller's ROLES to SCOPES and the framework // enforces the guard in the dispatch spine, before the executor. `voltro // check` can see the guard statically. // // The role map lives in `authz.ts`. Config-only — no external infra. import { defineEnv, envVar } from '@voltro/env' import { rbacPlugin } from '@voltro/plugin-rbac' import { demoRolesForTenant, roles } from './authz' export const env = defineEnv({ LOG_LEVEL: envVar.enum(['debug', 'info', 'warn', 'error'], { access: 'public', default: 'info' }), }) export default { type: 'api' as const, name: '{{capProjectName}}{{capAppName}}', store: 'memory' as const, plugins: [ rbacPlugin({ roles, // Demo: `ops` tenant → operator. Swap for a real resolver (subject // metadata / a DB lookup) when you wire operator auth — pair this api // with the api-auth / api-saas-starter auth backend. resolveRoles: (subject) => demoRolesForTenant(subject.tenantId), }), ], env, }