Skip to content

Configure a guardrail

View as Markdown

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

  • The answer-length guardrail from Write a guardrail, in the pack my-pack, with kindgi dev running and Kindgi 0.1's CLI.

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).

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

// 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 },
// …
};
Excerpt

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

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 passes:

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

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

"status": "completed",
…
"violations": [],
Excerpt

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

Terminal window
pnpm exec kindgi guardrails get my-pack.answer-length
"config": {
"maxChars": 300
},
Excerpt

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.

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 }:

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:

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:

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:

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.