# Configure a guardrail

> Give a guardrail's check its settings with config, describe them with a schema, and change them without touching the check.

This guide gives a guardrail's check its settings, so you change a limit without touching the check's code.

## Before you start

- **The answer-length guardrail** from [Write a guardrail](https://docs.kindgi.com/v0.1.10/guides/guardrails/write-a-guardrail.md), in the pack `my-pack`, with `kindgi dev` running and Kindgi 0.1's CLI.

## Declare the settings

A check's settings don't belong in its code: a limit, a list of words or a tool's id, for example. The guardrail declares them as `config`, and the check runs with them. Ask your coding agent, for example: "I want to change my guardrail's limit without touching its check." Kindgi's skills in your pack tell it how ([How it knows Kindgi](https://docs.kindgi.com/v0.1.10/start/coding-agents.md)).

For reference, the answer-length guardrail has one setting, `maxChars`:

- **TypeScript**

  ```ts excerpt
  // in guardrails/answer-length/index.ts
  export const check = defineCheck({
    id: 'my-pack.checks.answer-length',
    kind: 'zero-llm',
    configSchema: z.object({ maxChars: z.number().int().positive().default(500) }),
    evaluate: async (config, trace) => {
      // config.maxChars: 60 here, or 500 when the declaration gives none
      // …
    },
  });

  export default {
    id: 'my-pack.answer-length',
    // …
    check,
    config: { maxChars: 60 },
    // …
  };
  ```

  `configSchema` describes the settings. `config` in the declaration gives their values. The check's `evaluate` gets those values, typed from the schema.

- **Python**

  ```python excerpt
  # in guardrails/answer_length.py
  class Config(BaseModel):
      max_chars: int = Field(500, alias="maxChars", gt=0)


  @guardrail(
      id="my-pack.answer-length",
      # …
      config={"maxChars": 60},
  )
  def answer_length(config: Config, trace: RunTrace) -> CheckResult:
      ...
  ```

  The type of the check's first parameter describes the settings. `config=` gives their values, keyed by their wire names, which are the aliases: `maxChars`, not `max_chars`.

- **Java**

  ```java excerpt
  // in src/main/java/mypack/guardrails/AnswerLength.java
    public record Config(@JsonProperty(defaultValue = "500") @Positive int maxChars) {}

    public static final Guardrail<Config> GUARDRAIL = Guardrail.define("my-pack.answer-length")
        // …
        .config(Config.class)
        .set("config", Map.of("maxChars", 60))
        .check((config, trace) -> {
          // config.maxChars(): 60 here, or 500 when the declaration gives none
          // …
        });
  ```

  The `Config` record describes the settings: its fields' types and annotations make the schema, and `@JsonProperty(defaultValue = …)` a default. `set("config", …)` gives their values, keyed by their wire names. The check gets those values as a `Config`.

- **Scala**

  ```scala excerpt
  // in src/main/scala/mypack/guardrails/AnswerLength.scala
    final case class Config(@Positive maxChars: Int = 500)

    val guardrail: Guardrail[Config] = Guardrail[Config]("my-pack.answer-length")
      // …
      .set("config", Map("maxChars" -> 60))
      .check { (config, trace) =>
        // config.maxChars: 60 here, or 500 when the declaration gives none
        // …
      }
  ```

  The case class describes the settings: a field's default value is the schema's default. `set("config", …)` gives their values, keyed by their wire names. The check gets those values as a `Config`.

## Change a setting

A new value takes effect without a restart. Edit the value and save. The next turn uses it. With `maxChars: 300`, the `dev-echo` answer from [Write a guardrail](https://docs.kindgi.com/v0.1.10/guides/guardrails/write-a-guardrail.md#run-it) passes:

- **TypeScript**

  ```sh
  pnpm exec kindgi runs start --agent=my-pack.echo-agent --input='{"userMessage":"hi"}'
  ```

- **Python**

  ```sh
  uv run kindgi runs start --agent=my-pack.echo-agent --input='{"userMessage":"hi"}'
  ```

- **Java**

  ```sh
  ./kindgiw runs start --agent=my-pack.echo-agent --input='{"userMessage":"hi"}'
  ```

- **Scala**

  ```sh
  ./kindgiw runs start --agent=my-pack.echo-agent --input='{"userMessage":"hi"}'
  ```

`--agent` is the agent the guardrail is on, and `--input` its input.

```json excerpt
  "status": "completed",
  …
    "violations": [],
```

The registered guardrail shows the values it runs with. Get it by its id:

- **TypeScript**

  ```sh
  pnpm exec kindgi guardrails get my-pack.answer-length
  ```

- **Python**

  ```sh
  uv run kindgi guardrails get my-pack.answer-length
  ```

- **Java**

  ```sh
  ./kindgiw guardrails get my-pack.answer-length
  ```

- **Scala**

  ```sh
  ./kindgiw guardrails get my-pack.answer-length
  ```

```json excerpt
  "config": {
    "maxChars": 300
  },
```

## What the check receives

The check gets `config` resolved by its schema:

- **Defaults are filled in.** A guardrail without `config` runs with the schema's defaults; here, a limit of 500.
- **A value that doesn't fit is refused,** and the message names the setting. In TypeScript and Python that happens before any turn runs; in Java and Scala, on the turns the guardrail checks.

* **TypeScript**

  Zod's `.default()` and a JSON Schema's `default` both apply. When the pack is indexed, each guardrail's `config` is checked against its `configSchema`, so `kindgi dev` reports the file and `kindgi build` refuses the pack. With `config: { maxChars: -5 }`:

  ```text
  guardrails/answer-length/index.ts: guardrail my-pack.answer-length's config doesn't fit its configSchema at /maxChars: must be > 0
  ```

  A setting the schema requires, with no default, must be in `config`:

  ```text
  guardrails/answer-length/index.ts: guardrail my-pack.answer-length's config doesn't fit its configSchema: must have required property 'maxChars'
  ```

  In a deployment, a guardrail registered through the API, with `kindgi guardrails register`, names the pack's check with its own `config`. A config that doesn't fit the check's `configSchema` is refused with `422 guardrail-config-invalid`, and nothing is registered:

  ```text
  Error [invalid-request]: Guardrail "acme.strict-length" doesn't fit check "my-pack.checks.answer-length": config.maxChars must be > 0.
    ✗ /config/maxChars: config.maxChars must be > 0.
  ```

  Under `kindgi dev` the indexer has refused such a config already.

  The pack service also checks the config on every call, as sent, against the indexed `configSchema`. A registered guardrail whose config doesn't fit fails every turn it checks, whatever its action: the check doesn't run. `kindgi runs start` exits 1 with:

  ```text
  Error [run-failed]: input-validation-failed: Check "my-pack.checks.answer-length" config failed validation at /maxChars: must be > 0
  ```

  To fix it, unregister the guardrail and register it again with a config that fits.

* **Python**

  The check gets `config` parsed into its type:

  - **Defaults are filled in.** Give every field a default: a required field with no value isn't caught when the pack is indexed, and fails every turn the guardrail checks, whatever its action. `kindgi runs start` exits 1 with `Error [run-failed]: input-validation-failed: Check "my-pack.req-cfg" config failed validation: must have required property 'maxChars'`.

  - **Values are checked where they're declared.** A value that doesn't fit is a `DefinitionError`. `kindgi dev` reports the file, and so does `python -m kindgi.pack index`, which indexes the pack:

    ```text
    Guardrail "my-pack.answer-length": config does not fit its type: 1 validation error for Config
    maxChars
      Input should be greater than 0 [type=greater_than, input_value=-5, input_type=int]
    ```

  - **A key that isn't a wire name is ignored.** `config={"max_chars": 60}` isn't an error: the field keeps its default.

* **Java**

  The check gets `config` as a `Config`:

  - **Defaults are filled in.** A guardrail without `set("config", …)` runs with the schema's defaults; here, a limit of 500.

  - **A value that doesn't fit isn't caught before a turn.** Indexing the pack and `kindgi dev` accept `Map.of("maxChars", -5)`. The pack service checks the config on every call, so the check can't run: with `halt`, every turn the guardrail checks fails, and `./kindgiw runs start` exits 1 with:

    ```text
    Error [guardrail-violation]: Turn blocked: guardrail 'my-pack.answer-length' couldn't run its check (check-failed: guardrail "my-pack.answer-length": its check threw: input-validation-failed: Check "my-pack.checks.answer-length" config failed validation at /maxChars: must be > 0)
    ```

    With another action, the turn completes, and the error is recorded as [When the check can't run](https://docs.kindgi.com/v0.1.10/guides/guardrails/halt-or-record.md#when-the-check-cant-run) describes. Fix the value and save.

  - **`set("config", …)` is keyed by wire names**, as the schema names them: `maxChars`.

* **Scala**

  The check gets `config` as a `Config`:

  - **Defaults are filled in** from the case class's default values: without `set("config", …)`, a limit of 500.

  - **A value that doesn't fit isn't caught before a turn.** Indexing and `kindgi dev` accept `Map("maxChars" -> -5)`. The pack service checks the config on every call, so the check can't run: with `halt`, every turn the guardrail checks fails, and `./kindgiw runs start` exits 1 with:

    ```text
    Error [guardrail-violation]: Turn blocked: guardrail 'my-pack.answer-length' couldn't run its check (check-failed: guardrail "my-pack.answer-length": its check threw: input-validation-failed: Check "my-pack.checks.answer-length" config failed validation at /maxChars: must be > 0)
    ```

    With another action, the turn completes, and the error is recorded as [When the check can't run](https://docs.kindgi.com/v0.1.10/guides/guardrails/halt-or-record.md#when-the-check-cant-run) describes. Fix the value and save.

  - **`set("config", …)` is keyed by wire names**, as the schema names them.

## Next

- [Stop a turn or record a violation](https://docs.kindgi.com/v0.1.10/guides/guardrails/halt-or-record.md)
- [Use a built-in check](https://docs.kindgi.com/v0.1.10/guides/guardrails/use-a-built-in-check.md)
- [Write a guardrail](https://docs.kindgi.com/v0.1.10/guides/guardrails/write-a-guardrail.md)
