# Authentication, policy and scoped approval

Give each decision a clear owner and prove that denied requests never reach the protected handler.

Source context: Source-guided
Source: https://github.com/lssm-tech/contractspec/blob/d58fa5b7de53a947811e5cc68346be028ce18528/packages/foundation/contracts-runtime-kernel/src/operations/prepare.ts

## Separate the decisions

Authentication establishes who is calling. Policy decides whether that subject may perform a particular operation in context. Scoped approval supplies evidence that a specific request or class of effects has been reviewed. A ceremony requirement can require additional, fresh verification at execution time.

These mechanisms answer different questions. An authenticated user is not automatically permitted to perform every action, and a visible review button is not an approval receipt.

| Question | Mechanism | Host responsibility |
| --- | --- | --- |
| Who is calling? | Authentication context | Resolve identity through a trusted source |
| May this caller do this? | Policy decision | Supply and test the decision implementation |
| Has this request been approved? | Scoped approval | Verify the receipt against operation, subject, input and effects |
| Is fresh verification required? | Ceremony | Claim and complete the required host-bound ceremony |

## Declare the approval boundary explicitly

For an operation with execution.approval, the shared preparation path checks for approval evidence before handler entry. Missing evidence yields APPROVAL_REQUIRED with reason missing_receipt. If evidence exists but the host has no approval port, the reason is approval_runtime_unavailable.

Tags, presentation labels and MCP tool annotations describe an operation; they do not opt it into this enforcement. Configure the actual requirement and the trusted verifier.

## Bind evidence to the request

The approval port receives the operation key and version, subject context, parsed input, required effects and requested cost. Use those values to decide whether the supplied evidence authorizes this invocation, rather than treating the presence of a token as permission.

Connect has its own request, scope, digest, expiry and durable signature checks. Use the appropriate host integration for the boundary you are implementing; do not assume two differently typed receipt systems are interchangeable.

## Prove denial before success

Use a disposable handler with an invocation counter. Send valid input as an authenticated fixture subject without approval through REST and MCP. Assert the actual denial from each adapter and a counter that remains zero.

Then exercise the verifier you intend to ship: wrong subject, wrong scope or expiry should remain denied where those conditions apply. Only add an allowed path when its receipt is actually verified. A stub that always returns allow is useful for UI development, but it does not demonstrate authorization.

## Related examples

- [Stop a write before it runs](https://lssm.co/examples/required-approval/): Trace a REST request through an approval gate and inspect the zero-handler-call denial assertion.
- [Draft freely. Send with approval.](https://lssm.co/examples/draft-and-send-agent/): Follow one contract boundary through a workflow and an in-memory MCP transport.

