Guides

Policy packs (YAML)

Manage policies as YAML files alongside your code. Apply them to a project idempotently — perfect for CI / GitOps.

The dashboard is the right place to author policies interactively. The YAML pack format is the right place to keep them once you want them version-controlled, code-reviewed, and applied via CI.

Four packs ship in the repo at policies/:

FilePosture
dev-safe.yamlWorkspace agents — destructive shell tools blocked, network allowlist for github/npm, no secret-file reads.
finance-agent.yamlHard cap at $10k USD, mid-range transfers gated by HITL approval, audit CC on outbound email, ledger mutations refused.
agent-sandbox.yamlDefault-deny — short whitelist of read-only tools, shadow surveillance on email, catch-all deny.
hitl-and-steer.yamlDemonstrates approve (HITL) and steer (redirect) effects: hold prod DB writes for review, redirect prod writes to dev in staging.

Treat them as templates. Copy and edit the one closest to your shape.

Format

policies:
  - name: finance / hard-cap-10k-usd
    description: Refuse any USD transfer above 10,000.
    enabled: true # default true
    enforcement_mode: enforce # enforce | shadow
    effect: deny # allow | deny | modify | approve | steer
    conditions:
      - field: tool
        operator: equals
        value: transfer_funds
      - field: input.currency
        operator: equals
        value: USD
      - field: input.amount
        operator: greater_than
        value: 10000
    # only on effect: modify
    modifications:
      - field: input.cc
        value: [email protected]
    # only on effect: steer
    redirect_to:
      tool: send_email_via_audit
      input:
        audited: true

Field-by-field:

  • namenatural key inside the project. The loader uses it for idempotent upserts; pick stable, namespaced names (finance / hard-cap-10k-usd).
  • description — optional, surfaces in the dashboard.
  • enabled — boolean, default true.
  • enforcement_modeenforce blocks/modifies for real; shadow records the decision without affecting the call. See policy-engine.
  • effect — what the engine returns when the policy matches.
    • allow / deny — terminal decisions.
    • modify — overwrite input fields before executing (requires modifications).
    • approve — park the action in pending_actions for human review (HITL).
    • steer — redirect to a different tool (requires redirect_to).
  • conditions — non-empty array; all must match (AND).
  • modifications — required when effect: modify, ignored otherwise.
  • redirect_to — required when effect: steer. { tool, input } is the substitute call the SDK runs in place of the original.

Where the packs live

The curated starter packs (finance-agent.yaml, dev-safe.yaml, agent-sandbox.yaml, hitl-and-steer.yaml) live in the public klentlabs/klent-sdk-ts repo under policies/. Browse them on GitHub, fork into your own infra repo if you want them versioned alongside your code, or copy individual policies straight into the dashboard.

Easiest path for one-offs and for tweaking before applying:

  1. Open the pack on GitHub (e.g. finance-agent.yaml).
  2. Copy one policy's YAML block at a time.
  3. In app.klent.devPoliciesNew policy → paste-YAML mode (top-right of the form). Repeat for each policy in the pack.

The form parses the YAML, validates against the policy schema, and upserts on name — re-pasting an edited policy updates it in place rather than creating a duplicate.

Apply a pack — curl + yq (for CI)

The API's POST /v1/policies endpoint takes one policy at a time as JSON. To apply a YAML pack, split it into per-policy JSON and POST each one. Requires yq and jq:

PACK=finance-agent

curl -sf "https://raw.githubusercontent.com/klentlabs/klent-sdk-ts/main/policies/${PACK}.yaml" \
  | yq -o json '.policies[]' \
  | jq -c \
  | while IFS= read -r policy; do
      curl -fsS -X POST https://api.klent.dev/v1/policies \
        -H "Authorization: Bearer $KLENT_API_KEY" \
        -H "Content-Type: application/json" \
        -d "$policy"
    done

The API key already scopes the request to one project — no project_id in the URL. Each successful POST returns a pol_… ID.

Heads-up — the bare API is not idempotent. Re-running the loop against an unchanged pack will fail with a duplicate-name conflict on the second attempt. For idempotent automation, write a thin wrapper that GET /v1/policies, diffs against the pack, and PATCHes matches / POSTs misses — or use the dashboard's paste flow, which does the upsert for you. A native YAML import endpoint with built-in upsert/diff is on the roadmap.

In CI

For automation that needs to converge a project to the pack on every deploy, the simplest pattern is "delete then re-create" if the project is dedicated to this repo:

- name: Apply policy packs
  run: |
    for pack in finance-agent dev-safe; do
      curl -sf "https://raw.githubusercontent.com/klentlabs/klent-sdk-ts/main/policies/${pack}.yaml" \
        | yq -o json '.policies[]' \
        | jq -c \
        | while IFS= read -r policy; do
            curl -fsS -X POST "https://api.klent.dev/v1/policies" \
              -H "Authorization: Bearer ${KLENT_API_KEY}" \
              -H "Content-Type: application/json" \
              -d "$policy"
          done
    done
  env:
    KLENT_API_KEY: ${{ secrets.KLENT_API_KEY }}

Combine with DELETE /v1/policies/:id on a GET /v1/policies listing in a pre-step if you want a fully declarative state on every run.

Authoring tips

  • Allow before deny. First-match-wins evaluation means narrow allows short-circuit broad denies. Put your allowlist at the top.
  • Use shadow for new rules. Land the policy as enforcement_mode: shadow, let the timeline collect "would have blocked" matches for a few days, then flip to enforce when the rate looks right.
  • Name policies hierarchically. finance / cc-audit-on-email is searchable and groups by namespace in the dashboard list.
  • Keep packs small. A 50-line YAML is reviewable; a 500-line one isn't. Split by team or risk surface (finance.yaml, network.yaml, pii.yaml).