> ## Documentation Index
> Fetch the complete documentation index at: https://synthetiq.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Access verification

> The access matrix, derived concerns, and needs-attention flags

This page describes how access contracts are declared, checked against the code, and exercised. For how to review an access entry in the PRD view, see [Reviewing results](/docs/platform-docs/app-framework/prd/reviewing-results).

The **API Access & Rules** section of the PRD documents, for each endpoint: who can call it, whose rows it returns, and what it rejects. None of this is written by hand. Each endpoint declares a structured access contract, the build checks the contract against the code, and tests exercise every rule it declares.

## The access contract

Every backend procedure gets an entry declaring its access as structured data: whether the caller must be signed in, which scopes the procedure requires, and how rows are scoped to the caller: own rows only, rows reachable through a declared relationship (a reporting chain, a project membership), rows in the caller's org, or public. Attribute-based overrides ("admins can see others' rows") are declared against real columns, and bespoke rules the vocabulary can't express are named conditions.

Two properties keep the contract honest:

* **It must match the code's guards.** The declared authentication and scopes are cross-checked against the procedure's actual guard at build time. A contract claiming a scope the procedure doesn't enforce fails the build, and vice versa.
* **It must be exercised.** The build derives from the contract the exact set of scenarios the entry's test has to cover, and fails until each exists.

## Scenarios: every boundary gets a real request

The entry's test expresses each boundary as an isolated scenario: its own seed, real calls against the running backend as specific personas, and read-only verification of the outcome.

```typescript theme={null}
import { definePrdApiTest, expect } from "@synthetiq/app-framework/prd/api";

definePrdApiTest("tasks.list-only-own", (endpoint) => {
  endpoint.scenario("A user sees only their own tasks, never others'", { kind: "owner" }, async ({ seed, as, verify }) => {
    await seed({
      User: [{ id: "ada" }, { id: "ben" }],
      Task: [{ title: "A1", userId: "ada" }, { title: "B1", userId: "ben" }],
    });
    const rows = await as({ id: "ada" }).call("tasks.getMyTasks", undefined, { label: "Ada lists her tasks" });
    await verify("Ada sees only her own tasks, never Ben's", () => {
      expect(rows.every((r) => r.userId === "ada")).toBe(true);
    });
  });

  endpoint.scenario("Someone who isn't signed in is denied", { kind: "auth" }, async ({ as, expectRejected }) => {
    await expectRejected(() => as("anonymous").call("tasks.getMyTasks"));
  });
});
```

The derived checklist is thorough by construction. Signed-in required: prove the anonymous caller is denied. Scopes required: prove a signed-in caller without the scope is denied. Owner-scoped: prove the owner's call works, with its effect verified in the data, and that another user's doesn't. Relationship-scoped: prove the direct report is visible, the indirect one behaves as declared, and someone outside the chain is excluded. Each business rule: prove the boundary rejects it. The build names exactly which scenario is missing until the entry is fully covered.

Scenarios keep a strict shape so the evidence stays legible: seed once, then every state change goes through a real endpoint call (captured in the transcript). Database access inside the scenario is read-only, so a test can't write the expected result into the database and then assert it.

Business rules live here too, but only as **enforced rejections**: "at 3 active tasks, a 4th is rejected." A behavior the app performs is a feature, even when it's permission-flavored; see [PRD structure](/docs/platform-docs/app-framework/prd/prd-structure).

## Generated descriptions and review flags

From the verified contract, the build generates everything the reviewer reads:

* **"Who can do this" and "How access is verified"** — the plain-language description of each endpoint's rules is generated from the contract, not written by a person.
* **Concerns** — review flags computed from what the contract permits: an endpoint where any signed-in user can modify rows they don't own, or read every row, gets a **warning** that the permissive access should be verified. An unauthenticated surface is labeled "Anyone — even without signing in —". Nobody types a concern by hand, and nobody can forget one.

This is the property described in [The trust model](/docs/platform-docs/app-framework/prd/trust-model): a rule the code doesn't enforce fails its test, and the descriptions of enforced rules are generated rather than written.

## Needs attention

Concerns are a review signal, not a build failure. A permissive endpoint might be intended (a public read of a shared catalog) or an oversight. The build stays green either way, and the PRD view surfaces every flagged entry under a **need attention** count in the header, next to current, needs re-recording, and failing.

The reviewer decides: confirm the permissive access is intended, or send it back to be locked down. See [Reviewing results](/docs/platform-docs/app-framework/prd/reviewing-results). The system's guarantee is that every permissive endpoint gets flagged for that decision.
