// Streaming subscription: every change to `notes` for the caller's // tenant lands as a delta. The runtime AND-merges tenant scope into // the predicate via the `tenant()` mixin on the table — no manual // `eq('tenantId', ...)` needed in the handler. import { defineQuery } from '@voltro/protocol' import { Schema } from 'effect' export const listNotes = defineQuery({ name: 'notes.list', // Every wire-exposed procedure must declare an access decision — `guards:` or // `openAccess:` — or the app refuses to boot (`security.defaultDeny`). This // one is open, and the reason says what that buys and what it does not: // // `tenant()` confines the result to the tenantId on the request and re-checks // it on every delivery, so one tenant's rows never reach another's // subscription. But this template configures NO auth strategy, so that // tenantId comes from the caller's own `x-tenant` header — it shapes the // result, it does not authorize the caller. A `guards: [{ scope }]` here // would be unsatisfiable: with no auth strategy and no rbac, every caller // resolves to an anonymous Subject that holds no scopes, so the guard would // deny 100% of traffic. That is not strict security, it is an outage. // // So: add an auth strategy (see the `api-auth` template) FIRST, then swap // this line for a `guards:` in the same change — at that point `tenant()` // becomes real isolation because the tenant comes from a verified session. openAccess: 'lists notes for the request\'s tenant only (`tenant()` scopes every delivery). No auth ' + 'strategy ships in this template, so that tenant comes from the caller\'s own `x-tenant` ' + 'header — result shaping, not access control. Add a strategy, then a `guards:`.', input: Schema.Struct({}), output: Schema.Array( Schema.Struct({ id: Schema.String, title: Schema.String, body: Schema.String, done: Schema.Boolean, tenantId: Schema.String, createdAt: Schema.Date, }), ), })