# fairvisor validate

- Canonical URL: https://docs.fairvisor.com/docs/cli/validate/
- Section: docs
- Last updated: n/a
> Validate a policy bundle JSON file before deployment.


`fairvisor validate` parses and validates a policy bundle file, reporting every structural error found. It does not start the edge or contact the SaaS API.

## Synopsis

```
fairvisor validate <file|->
```

Pass `-` to read from stdin.

## What is validated

The validator checks the complete bundle schema:

1. **Top-level structure** — `bundle_version` present and > 0, `policies` array non-empty, optional `issued_at`/`expires_at` are valid ISO 8601
2. **Each policy** — `id` non-empty, `spec` present, `selector` valid, `mode` is `enforce` or `shadow`
3. **Each selector** — at least one of `pathExact` or `pathPrefix` present, both start with `/`, `methods` are valid HTTP verbs, optional `hosts` is a non-empty string array
4. **Each rule** — `name` non-empty, `limit_keys` non-empty array of valid descriptor keys, `algorithm` is a known value, `algorithm_config` matches the algorithm schema
5. **Token bucket config** — one of `tokens_per_second` / `rps` present, `burst >= tokens_per_second`
6. **Cost-based config** — `budget > 0`, `period` is `5m`/`1h`/`1d`/`7d`, `staged_actions` non-empty, strictly ascending thresholds, a `reject` action at 100%
7. **Kill switches** — `scope_key` matches `(jwt|header|query|ip|ua):[A-Za-z0-9_-]+`, `scope_value` non-empty
8. **Runtime overrides** — optional `global_shadow` and `kill_switch_override` blocks must be valid objects; when `enabled=true`, `reason` and future `expires_at` are required
9. **Loop detection** — `window_seconds` positive integer, `threshold >= 2`
10. **Circuit breaker** — `spend_rate_threshold_per_minute` positive

Errors are reported as `path: message`:

```
policies[0].spec.rules[0].algorithm_config: burst must be >= tokens_per_second
policies[1].spec.selector: at least one of pathExact or pathPrefix is required
```

## Examples

```bash
# Validate a file
fairvisor validate policy.json

# Validate from stdin (e.g. piped from another command)
cat policy.json | fairvisor validate -

# Use in CI — non-zero exit on error
fairvisor validate policy.json && echo "Policy OK" || exit 1
```

## Successful output

```
Valid: 2 policies, 5 rules
```

## Failed output

```
Error: invalid bundle
  policies[0].spec.rules[0].algorithm_config: burst (5) must be >= tokens_per_second (10)
  policies[1].id: must be a non-empty string
```

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Bundle is valid |
| `1` | Bundle is invalid (errors printed to stderr) |
| `1` | File not found or unreadable |
| `1` | JSON parse error |

## CI integration

```yaml
# GitHub Actions example
- name: Validate policy
  run: fairvisor validate policy.json
```

## Standalone CI (no SaaS)

If you run Fairvisor in standalone mode (local `policy.json`, no SaaS control plane), make policy validation a required CI step before deploy.

### GitHub Actions

```yaml
name: Validate Fairvisor Policy
on: [push, pull_request]

jobs:
  validate-policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Fairvisor CLI
        run: |
          curl -fsSL https://raw.githubusercontent.com/fairvisor/edge/main/install.sh | sh
      - name: Validate policy
        run: fairvisor validate policy.json
```

### GitLab CI

```yaml
stages:
  - validate

validate_policy:
  stage: validate
  image: alpine:3.20
  script:
    - apk add --no-cache curl
    - curl -fsSL https://raw.githubusercontent.com/fairvisor/edge/main/install.sh | sh
    - fairvisor validate policy.json
```

Failing validation must block merge/release for standalone deployments.

