Example

Worked application · access check

A support route missing its role check

A delivery company gives customers a case view and its support team a workspace with private notes. The application logic is written in Bosatsu, a typed functional language whose compiled structure Yichus can inspect. A maintenance shortcut accidentally makes the privileged review helper reachable from an ordinary authenticated route.

This is a complete five-route component. You can run its customer behavior, inspect both full programs, reproduce the static finding, make edits, and hand the repair to a connected agent in the same page.

Application and access requirements

Customers use their normal account. Support workers use the staff workflow. One support_cases table holds the owner id, public case fields, and a private staff note.

  1. A customer may view or reply only when owns_case confirms the stored case names that caller.
  2. A support worker may review the full case and resolve it only through routes requiring support.
  3. /staff/quick-review remains available with the same staff role requirement.
  4. Route declarations continue to list the reads and writes their handlers perform.

The policy stays GuardedOrRoles(["owns_case"], ["support"]) before and after the repair. It permits the customer path under the ownership guard and the staff path when every reaching route requires the role.

Five routes over one case record
RouteUserAccess path
/cases/viewCustomerRead a row, use it only after owns_case
/cases/replyCustomerRead a row, then use and write it after owns_case
/staff/reviewSupportRole-protected private review
/staff/resolveSupportRole-protected read and write
/staff/quick-reviewSupportThe route repaired below

Read the committed requirements.

The flawed application

The ordinary route still requires some authenticated caller at the server boundary, but it does not require that caller to have the support role. It reaches review_case, then load_for_staff, which returns the private note without the customer ownership guard.

route("/staff/quick-review", handler("review_case", review_case),
  [ReadPerm("support_cases")]),

Open the complete flawed Bosatsu source. The other four routes, both customer handlers, the staff handlers, the table, and the access policy are all present in that file.

The exact check and its recorded result

The featured request runs api_access_check on the complete flawed source. It is a static analysis over typed compiled IR; it does not execute a login or sample users.

api_access_check({
  "sources": [{
    "fileName": "support-access-before.bosatsu",
    "source": COMPLETE_LINKED_SOURCE
  }]
})

Exact request with the full source · complete captured response

Tool report — recorded: support_cases is violated. The response includes this verbatim finding:

{
  "binding": "load_for_staff",
  "resource": "support_cases",
  "operation": "use",
  "kind": "RowEscapesGuard",
  "status": "violated",
  "detail": "a value derived from a row of \"support_cases\" is returned outside any branch on owns_case applied to Principal.user_id and the row"
}

Author’s interpretation: the row escape is expected inside the privileged staff helper, but the role bypass cannot apply because an ordinary route also reaches that helper. The report does not say that authentication is absent. It says the declared ownership-or-role policy is not established on every path.

Repair the route, preserve the policy

The repair changes only the shortcut declaration. It keeps the useful route, handler behavior, permissions, ownership guard, and access policy.

- route("/staff/quick-review", handler("review_case", review_case),
+ role_route("/staff/quick-review", handler("review_case", review_case), ["support"],
    [ReadPerm("support_cases")]),

Open the complete repaired source.

The same check, with the repaired source and no changed assumptions, returns proven: true and marks support_cases holds.

Exact repaired request · complete captured response

Tool report — recorded:

{
  "binding": "load_for_staff",
  "resource": "support_cases",
  "operation": "db_read_opt",
  "kind": "RoleAuthorityProven",
  "status": "holds",
  "detail": "every route reaching this binding requires every role (support)"
}

Author’s interpretation: the compiled route graph now gives the helper the declared role authority. The customer read remains guarded and the customer write remains dominated by owns_case.

On the same repaired component, api_permissions compared each route’s declared operations with the CRUD effects inferred from compiled IR; all five routes are recorded as match. Exact permissions request · captured response

Run, edit, repair, and reset

Open the drawer. Run function executes the compiled customer handler as Alice against the displayed rows. Change p.user_id to mallory to exercise the denial. Check access runs the static checker. Switch between both complete versions, edit a copy, and use Reset example to restore the recording source.

Code & run Customer support access before and after
Ask a connected agent to make the repair

Open Work with your agent inside the drawer, connect this tab through WebMCP, and send:

Call yichus_example_read for exampleId "support_access". Preserve the requirements and GuardedOrRoles policy. Run the current customer handler for Alice and Mallory, then run the access check. Repair the route-authority finding without removing the convenience route or weakening the policy. Use revision-aware updates, rerun both behaviors and the access check, and read yichus_example_feedback before reporting what changed.

The page tools reject stale revisions and return the visible result after an edit. The agent is editing this browser example, not silently changing a repository checkout.

Recorded behavior checks

These are direct executions of compiled handlers in a fresh seeded in-memory world. Alice and Mallory are browser test inputs. The staff run calls review_case directly, so it bypasses route dispatch and does not demonstrate HTTP authentication or role enforcement.

One execution per displayed input, seed 41
RunRecorded responseEvidence
Alice views her case200 · public case fields, no staff noteresponse
Mallory views Alice’s case403 · no case fieldsresponse
Direct staff review200 · includes the private noteresponse
Alice requests a missing case404response
Alice replies to her case200 · stored reply appears in the responseresponse
Mallory replies to Alice’s case403 · seeded row remains unchangedresponse
Direct staff resolution200 · resolved with the new private noteresponse

What changed, and what the result establishes

The static result establishes the declared GuardedOrRoles policy for every supported operation in this compiled source: customer row uses sit under owns_case, and every route reaching the unguarded staff operations requires support. The recorded runs separately show the intended responses for seven concrete inputs, including both write paths, a denied write, and a missing case.

The result assumes the server engine authenticates requests, supplies the correct Principal, and enforces role_route before handler invocation. It does not inspect a production identity provider, database, or reverse proxy. It also does not decide whether the names owns_case and support express the organization’s real-world policy.

The IO audit separately reports read-then-write patterns in reply_to_case and resolve_case. The worked example does not claim concurrent-update safety: two writers can overwrite each other unless the deployed store serializes them. A production version should use the transaction contract or an appropriate compare-and-set operation for those updates. The access result neither proves nor weakens that separate concurrency requirement.

Recording provenance, source hashes, engine artifact hash, method, seed, and parameters · desktop capture · mobile capture · Access-check semantics and trusted boundaries

Where this fits

What this is about: Safety and Permissions