# Author a Policy

> Build deterministic executable authorization logic and test allow, deny, and constraint behavior before attachment.

## Before you begin {#before-you-begin}

Write down the one authorization-target kind and selector the Policy should govern, the normalized identity evidence it may use, and the constraints it should add. A target may be a managed Resource or a platform address such as a session, binding, or deployment. Executable Policy code is deterministic: it has no network, secret, storage, clock, or random authority. Use request input only.

## Steps {#steps}

1. Create an ESM package and install the SDK with `npm install @constal/sdk`.
2. Export a Policy definition:

   ```ts src/index.ts
   import { policy, type PolicyOutcome } from "@constal/sdk";

   export default policy({
     id: "ticket-read-boundary",
     version: "1.0.0",
     evaluate(input): PolicyOutcome {
       const isRead = input.context["resource.operation"] === "ticket.read";
       return isRead
         ? { kind: "constrain", constraints: [
             { kind: "context", equals: { "request.region": "us" } },
           ] }
         : { kind: "deny", code: "read-only", reason: "read only" };
     },
   });
   ```

3. Add `constal.policy.json` with the same id and version, its entrypoint, namespace, and one required target selector:

   ```json constal.policy.json
   {
     "schemaVersion": 3,
     "kind": "policy",
     "id": "ticket-read-boundary",
     "namespace": "default",
     "version": "1.0.0",
     "entry": "src/index.ts",
     "target": {
       "resourceKind": "service",
       "selector": {
         "matchLabels": { "access": "support" }
       }
     },
     "policies": []
   }
   ```

   The selector uses the same normalized `matchLabels` and `matchExpressions` semantics as Channel-to-Agent selection. It cannot cross the authenticated environment, tenant, or namespace boundary. Use the protected `constal.ai/crn` label when exactly one Resource should match.
4. Unit test allowed, denied, malformed, and boundary inputs with `evaluatePolicyDefinition()`. The execution environment—not Policy code—validates the outcome and creates protocol hashes.
5. Upload the archive through the common deployment workflow.

## Verify {#verify}

Call `POST /v1/namespaces/:namespace/policies/:id/evaluate` with a representative action, deployed Resource, and optional context. This endpoint is explicitly a non-enforcing simulation: confirm `simulation: true`, `enforcing: false`, the exact target hash, decision, and Policy hash. Admission resolves and evaluates its own accepted snapshot; it never reuses a simulation result.

## Next steps {#next-steps}

Continue with [Operate Policies](/docs/policies/operate.md) to inspect its selected Resources. See [Govern Resource usage](/docs/policies/governance.md) for controls and limits, [Policies and analytics](/docs/sdk/policies-and-analytics.md) for the SDK boundary, and [Deploy an Agent](/docs/agents/deploy.md) for the shared package upload model.
