> For the complete documentation index, see [llms.txt](https://govplane.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://govplane.gitbook.io/docs/documentation/cli/cli-toolkit-commands.md).

# CLI Toolkit Commands

The commands in `@govplane/cli-toolkit`. These are the ones that **write**.

All of them accept the [global options](/docs/documentation/cli/basic-cli-commands.md#global-options) and all of them run offline.

## `govplane analyze`

Statically analyses your source for Govplane `evaluate()` calls and writes a reviewable draft.

```bash
govplane analyze --source .
govplane analyze --source ./src -w ./governance
govplane analyze --check                       # CI mode
govplane analyze --interactive                 # step through each discovery
govplane analyze --merge                       # add to an existing draft
govplane analyze --bundle ./policy-bundle.json # compare against what exists
```

```
Govplane Analysis

Source:
  src
  1 file scanned

Discovered 1 policy:

  missing  auth-login-authenticate
     auth / login / authenticate
     login.ts:1

Draft:
  governance/policy-drafts.json

Result:
  Analysis completed successfully

Drafts carry no rules — analyze never invents them.
Add rules with "govplane policies add-rule", then run "govplane build".
```

It is real parsing, not pattern matching: an `evaluate(` inside a comment or a string is not reported, and calls are recognised either by **binding** (the receiver traces back to a Govplane import) or by **shape** (a recognisable `target` at the call site). Each discovery carries a confidence — `high`, `medium` or `low` — recording how much of the target resolved statically.

{% hint style="warning" %}
**`analyze` never invents a rule.** It finds where policy is evaluated and writes policies with `rules: []`. Deciding what the answer should be is your job — that is the whole point of the separation.
{% endhint %}

{% hint style="info" %}
`analyze` refuses to overwrite an existing draft. Run it **before** `working-folder init`, or pass `--merge` to add new discoveries while keeping what is there, or `--force` to replace it.

`--bundle` paths are resolved from the **working folder**, not the current directory.
{% endhint %}

`--check` is the CI form: it exits non-zero when a discovered evaluation point is not covered by a bundle, which turns "somebody added an `evaluate()` and forgot the policy" into a failed build.

## `govplane policies`

Creates and edits drafts without hand-editing JSON.

```bash
govplane policies create-file
govplane policies list
govplane policies add-policy    --policy-key login-protection --defaults-effect allow
govplane policies update-policy --policy-key login-protection --defaults-effect deny
govplane policies remove-policy --policy-key login-protection
govplane policies add-rule      --policy-key login-protection --rule-file ./deny-retries.json
govplane policies update-rule   --policy-key login-protection --rule-id r1 --rule-json '{…}'
govplane policies validate --strict
```

```
Govplane Policies

Draft file: governance/policy-drafts.json

Policies: 1

KEY                      ACTIVE VERSION  DEFAULT EFFECT  RULES
auth-login-authenticate  1               allow           1
```

Policy defaults are set with flags, one group per effect type:

```bash
--defaults-effect allow
--defaults-effect deny
--defaults-effect kill_switch --kill-switch-service payments --kill-switch-reason "INC-4421"
--defaults-effect throttle    --throttle-limit 100 --throttle-window-seconds 60 --throttle-key tenant
--defaults-effect custom      --custom-effect '{"tier":"free"}'
```

Rules are supplied as JSON, from a file or inline — they have more structure than flags carry well:

```bash
govplane policies add-rule --policy-key login-protection --rule-file ./deny-retries.json
govplane policies add-rule --policy-key login-protection --rule-json '{"id":"r1",…}'
```

`--versioned` writes to the next versioned draft file instead of editing in place. See [Policy Drafts Schema](/docs/documentation/schemas/policy-draft.md) for the document these commands produce.

## `govplane build`

Compiles drafts into a deterministic, validated runtime bundle. Local files only; never contacts Govplane.

```bash
govplane build
govplane build --draft ./policy-drafts.json --output ./dist/bundle.json
govplane build --env staging --org-id org_1 --project-id proj_1
govplane build --signed --hmac-secret-env GOVPLANE_HMAC_SECRET
govplane build --report --strict
```

```
Output:
  Bundle: governance/policy-bundle.json

Integrity:
  Checksum: sha256:b65a57eab43aa8b697ae4a56a276bb38ee4d7f77d545b690cfea7bb403bdf37f
  ETag: "b65a57eab43aa8b697ae4a56a276bb38ee4d7f77d545b690cfea7bb403bdf37f"

Signature:
  Enabled: yes
  Algorithm: HMAC_SHA256
  Key ID: local-key-01

Result:
  Build completed successfully
```

**Deterministic** means the same draft produces byte-identical output: policies and rules are sorted, keys are ordered, and the checksum covers a canonical projection rather than the file. Re-versioning a bundle therefore does not invalidate its signature.

Building **archives** the previous bundle beside the new one and increments `bundleVersion`, counted per `orgId` / `projectId` / `env`:

```
governance/
├── policy-bundle.json                              ← current, bundleVersion 4
├── policy-bundle.2026-08-08T15-18-56-926Z.json
└── policy-bundle.2026-08-08T15-18-21-352Z.json
```

Signing options are shared with `sign` — see [Configuring the Toolkit](/docs/documentation/cli/configuring-the-cli-toolkit.md).

## `govplane sign`

Signs an existing unsigned bundle, using the same signing engine as `build --signed`.

```bash
govplane sign --signing-algorithm HMAC_SHA256 --hmac-secret-env GOVPLANE_HMAC_SECRET
govplane sign --signing-algorithm ECDSA_SHA_256 --ecdsa-private-key ./keys/signing.pem
govplane sign --output ./dist/policy-bundle.signed.json --force-output
```

`--signing-algorithm` is required. Without `--output` the bundle is signed **in place**.

This exists so building and signing can be separate pipeline stages with different trust boundaries: a build job that never sees a key, and a release job that does.

## `govplane simulate`

Evaluates targets against a bundle or draft **using the real SDK engine**, so what you see here is what your application will get.

```bash
govplane simulate --service auth --resource login --action authenticate
govplane simulate --service auth --resource login --action authenticate \
  --context '{"failedAttempts":6}' --trace full
govplane simulate --suite ./simulations/auth-suite.json
govplane simulate --scenario ./simulations/login-blocked.json --format json
```

```
Context:
  failedAttempts: 6

Decision:
  decision: deny
  reason: rule

Match:
  Policy: auth-login-authenticate
  Rule: deny-after-five-failures
```

`--trace full` explains the answer, including what was ruled out and why:

```
Evaluation trace:
  Policies seen: 1
  Rules seen:    1
  Matched:       0

  Rules considered:
    auth-login-authenticate / deny-after-five-failures  priority 100  skipped (when_false)

  Selected:
    auth-login-authenticate / __default__ (priority -1, effect allow)
```

`__default__` and `priority -1` both mean the decision came from a policy default rather than a rule.

Context can be given three ways: `--context '<json>'`, `--context-file <path>`, or repeated `--context-value key=value`.

### Scenario files

A scenario is a named target, a context, and optionally what you expect:

```json
{
  "name": "locked out after five failures",
  "target": { "service": "auth", "resource": "login", "action": "authenticate" },
  "context": { "failedAttempts": 6 },
  "expected": { "decision": "deny", "reason": "rule", "ruleId": "deny-after-five-failures" }
}
```

A suite is a list of them:

```json
{ "name": "auth", "scenarios": [ { "name": "…", "target": {…}, "context": {…} } ] }
```

Only the fields you declare in `expected` are checked, so a scenario asserting a `decision` does not start failing because a rule was renamed. This is what makes suites usable as **policy regression tests in CI**.

{% hint style="info" %}
`simulate` verifies the bundle's signature before evaluating it, using the key pinned in configuration. `--skip-signature-verification` bypasses that — appropriate for a bundle you just built locally, not for one you received.
{% endhint %}

## `govplane activate` and `govplane license`

Covered in [Installing the Toolkit](/docs/documentation/cli/installing-the-cli-toolkit-extension.md) and [Licensing](/docs/documentation/cli/licensing.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://govplane.gitbook.io/docs/documentation/cli/cli-toolkit-commands.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
