# {{projectName}} / {{appName}}

Voltro API scaffold (template: **api-feature-flags**) — feature flags with
`@voltro/plugin-flags`: kill-switches, % rollout, targeting, and two ways to
gate rpc calls.

## Boot

```bash
pnpm install
pnpm --filter @{{projectName}}/{{appName}} dev
# → http://localhost:4000  (store: memory — zero infra)
```

## What this shows

- **Flags as code** — declared in `app.config.ts`: a bare boolean is a
  kill-switch (`newEditor: true`, `betaExport: false`); a rich definition adds
  a deterministic `% rollout` (`gradualRollout: { rollout: 25 }`) and
  OR-of-rules `targeting` (`proDashboard: { targeting: [{ metadata: { plan:
  'pro' } }] }`).
- **Two ways to gate** —
  - **Declarative** (`gatedBy`): `notes.export` is mapped to `betaExport`. The
    framework fails typed `FlagDisabled` BEFORE the handler runs when the flag
    is off — no code in the handler.
  - **In-handler** (`requireFlag`): `notes.create` calls
    `requireFlag(ctx, 'newEditor')`, the Effect-native guard (same shape as
    `requireScope` / `requireEntitlement`).
- **Typed on the client** — both paths fail `FlagDisabled`, declared on the
  procedure's `error:`, so the browser decodes it typed.

## Try it

```bash
# newEditor is ON → requireFlag passes → note created:
curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
  -d '{"tag":"notes.create","input":{"tenantId":"acme","title":"Flagged note","body":"hi"}}'

# betaExport is OFF → gatedBy blocks before the handler → typed FlagDisabled:
curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
  -d '{"tag":"notes.export","input":{}}'
# → { ok:false, error:{ _tag:"FlagDisabled", flag:"betaExport" } }
```

Flip `betaExport: true` in `app.config.ts` (or toggle it at runtime with
`store: 'postgres'`) and `notes.export` starts succeeding — no code change.

## On the web side

```tsx
import { useFlag } from '@voltro/plugin-flags/web'
const showBeta = useFlag('betaExport')      // evaluated for the signed-in subject
{showBeta && <BetaExportButton />}
```

`useFlag` reads the synthesized `flags.evaluate` query, so client UI gating
uses the SAME evaluation (targeting + rollout) as the server guards.

## Runtime toggles

Set `store: 'postgres'` to overlay runtime-toggleable flags (in
`_voltro_feature_flags`) on the code baseline — flip a kill-switch without a
deploy. The dashboard ships a **Flags** panel for exactly this.
