Experimental (3.0 Phase 1). The signable org standard, distinct from project config and from the 2.x agent-write pre-approval.
kit 3.0's model-change is that the standard becomes a first-class, signable
document an org owns — separate from per-project config — that a kit identity
signs and any kit can verify offline before enforcing. "identity + policy = the
contract."
version = 1
require_triage = true # no untriaged dependency installs
required_scanners = ["trivy", "trufflehog"] # these MUST run; a missing one fails the gate
prod_writes_need_approval = true
min_kit_version = "2.2.0"
[thresholds]
code_health = 7.5 # e.g. CodeScenekit policy init scaffolds it. kit policy validate checks it against the
allow-listed schema. version is required and a doc declaring a version newer than
this kit understands is refused (upgrade kit).
kit policy sign # sign with this machine's kit identity → .kit-policy.sig
kit policy verify # verify against locally-known keys (current + rotated)
kit policy verify --key <spki-pem|file> # pin the expected org key
- Signing is over canonical JSON (recursively key-sorted) of the parsed document, so the signature survives TOML reformatting, comments, and key reordering — it breaks only on a real policy change.
kit policy signrefuses to sign an invalid policy (a signature must vouch for a sound doc).verifyfails if the policy changed since signing (fingerprint mismatch) or the signature is invalid; it fail-opens (a warning, not a failure) when the signer key is unknown — pin it with--key. A signature by a revoked key (seekit panic) fails.
Commit both .kit-policy.toml and .kit-policy.sig; they travel with the repo,
and an org distributing one signed policy across many repos verifies the same way
everywhere.
.kit.toml [policy.agent_writes] (2.x, see src/policy.ts) is the per-repo
agent-write pre-approval — which vendor operations the operator pre-authorized.
.kit-policy.toml is the org-level standard (thresholds / requirements),
versioned and signed independently of project config. They are complementary
layers.
[policy.agent_writes]is ENFORCED as of 6.3.2, and reaches the plugin write surfaces from 6.4.0. It was declarative through 6.3.1 — parsed, folded intoKIT_POLICY_HASH, and consulted by nothing — and this note said so. What it enforces now:
- Inside kit, at
propagate()'s choke point and insecrets-rotate-cli.ts, deciding via the singlepolicyDecisionand writing apolicy-checkaudit event for every refusal AND every grant, with the vendor, op, state and policy hash.- In the
kit-plugin-*packages, which kit-core cannot call: kit resolves the block and exports the refusals asKIT_POLICY_DENY, and each plugin's write surface refuses what is in it. The plugin never sees the config, only the decision.It only ever NARROWS. The block is unsigned config in
.kit.toml, so anyone who can edit the repo — including an agent — could add a line to it. Declaring an op therefore cannot SATISFY a gate; elevation, read-only and signed approval remain authoritative.approval.tsis the grant-shaped mechanism, and it requires an org-authority signature. That is the difference.Two limits worth knowing before relying on it. An empty list is a LOCK, not a wildcard:
stripe = []declares the vendor and pre-approves nothing, so every Stripe op is refused — and by the same rule a typo (env-setforenv_set) turns a pre-approval into a blanket denial for that vendor, which is whykit checkreports an unrecognised op as thepolicy agent-writesrow. And the plugin-side channel is an environment variable, exactly as strong asKIT_READ_ONLY: a process that never ran kit sees no denials, and a plugin-side refusal is not audited, because a plugin has no path to the governed project's log.
kit policy check # evaluate the signed policy against this machine's state
kit policy check --strict # a missing required scanner also fails
kit policy check --json # machine-readable report + exit code (for CI)
kit policy check verifies the signature first (the trust anchor), then evaluates
the machine-checkable requirements and prints a per-requirement verdict:
| Requirement | How it's checked |
|---|---|
| signature | authentic? (warn if unsigned/unknown signer; fail if tampered or revoked) |
min_kit_version |
current kit version ≥ required (deterministic) |
required_scanners |
each resolvable mise-first (warn if missing; fail under --strict) |
prod_writes_need_approval |
.kit.toml [governance.approval].production_writes is set |
require_triage |
reported — enforced at runtime by the install-gate (not duplicated) |
thresholds |
reported — enforced by the relevant data-source plugin (e.g. CodeScene) |
It is opt-in: with no .kit-policy.toml it is a no-op (exit 0). A hard failure
(non-zero exit) is a tampered/revoked signature, an invalid schema, an unmet
min_kit_version, or — under --strict — a missing required scanner. Run it as a
CI step (kit policy check --strict) to gate on the signed org standard.
A locally-signed policy only verifies on the machine that signed it. To distribute
ONE org standard across MANY repos, commit a trust anchor — .kit-policy.signers
— listing the org public key(s) allowed to sign the policy:
kit identity show --public > org.pub # on the org's signing machine
kit policy trust org.pub --label acme-security # in each repo (commit the result)
kit policy trust --list # show trusted signers
kit policy trust --remove <kid> # revoke trust in a signer
verifyPolicy resolves the signer key in trust order: a pinned --key → this
machine's own identity → the committed org anchor. So a policy signed by the org
key verifies as valid (org trust anchor) on any clone — asymmetric, no shared
secret, only public keys distributed.
Fail-closed once anchored. With a .kit-policy.signers present, a policy whose
signer is NOT in it is a hard fail in kit policy check / kit ci (not a
warn) — distribution must mean enforcement, the same discipline as the HMAC audit
anchor. Without an anchor, an unknown signer stays a warn (trust-absence ≠ forgery).
Signed org bundles (packaging the policy + signer manifest for drop-in) and RBAC keyed to identity (which role may read/write/elevate/install/deploy) — Phase 2 depth.