> For the complete documentation index, see [llms.txt](https://docs.gomboc.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gomboc.ai/policy-management/policy-sets/policy-sets-backup-and-version-control-via-api.md).

# Policy Sets: Backup & Version Control via API

Gomboc's Policy Sets can be fully managed through the **Rules Service API**, giving you programmatic access to export, back up, and restore your policy configuration. This is useful for:

* **Disaster recovery** — quickly restore your configuration after accidental changes.
* **Environment promotion** — copy policy sets from a staging org to production.
* **Audit trails** — track every change to your policy configuration in Git history.
* **GitOps workflows** — store the desired state of your policy sets in a repository and reconcile it on a schedule.

***

### Prerequisites

#### Personal Access Token (PAT)

All Rules Service API calls require a Bearer token. Generate one from the Gomboc Portal:

1. Open the account dropdown (top-right corner) → **Settings**.
2. Select **Personal Access Tokens**.
3. Click **Generate Token**, give it a name and an expiry, then click **Create**.
4. Copy the token somewhere safe — it is shown only once.

Set it as an environment variable for the examples below:

```bash
export GOMBOC_TOKEN="<your-personal-access-token>"
```

The scripts on this page also require [`jq`](https://stedolan.github.io/jq/) and `curl`.

#### API Base URL

All Rules Service endpoints are served at:

```
https://rules.app.gomboc.ai
```

***

### Concepts

In the Gomboc Portal, a **Policy Set** is shown under **Policy Management → Policy Sets**.

Internally — and through the API — each policy set is a **Channel**: a named, saved search over the policy library that determines which rules are applied to your workspaces.

A channel has the following fields:

| Field         | Type              | Description                                               |
| ------------- | ----------------- | --------------------------------------------------------- |
| `name`        | string            | Unique identifier (also used as the human-readable label) |
| `query`       | string (optional) | ORL search query that selects rules                       |
| `filters`     | array of strings  | Additional filter expressions                             |
| `annotations` | object (optional) | Arbitrary key/value metadata tags                         |

***

### Exporting Policy Sets (Backup)

Use `GET /api/v1/channels/search` to retrieve all policy sets defined in your account. The endpoint supports pagination — iterate over pages until you have collected every record.

#### Shell export script

```bash
#!/usr/bin/env bash
# export-policy-sets.sh — exports all policy sets to policy-sets-<date>.json
set -euo pipefail

BASE_URL="https://rules.app.gomboc.ai"
PER_PAGE=1
USE_DATE=1

usage() {
  cat <<EOF
Usage: $(basename "$0") [--no-date]

Exports all policy sets to policy-sets-YYYY-MM-DD.json by default.
Use --no-date to write policy-sets.json instead.
EOF
}

while [ $# -gt 0 ]; do
  case "$1" in
    --no-date)
      USE_DATE=0
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Error: unknown option: $1" >&2
      usage >&2
      exit 1
      ;;
  esac
done

if [ "$USE_DATE" -eq 1 ]; then
  OUTPUT="policy-sets-$(date +%Y-%m-%d).json"
else
  OUTPUT="policy-sets.json"
fi

if [ -z "${GOMBOC_TOKEN:-}" ]; then
  echo "Error: GOMBOC_TOKEN is not set" >&2
  exit 1
fi

decode_jwt_payload() {
  local token="$1"
  local payload padded rem

  payload=$(printf '%s' "$token" | cut -d. -f2)
  if [ -z "$payload" ]; then
    return 1
  fi

  padded="$payload"
  rem=$((${#padded} % 4))
  case "$rem" in
    2) padded="${padded}==" ;;
    3) padded="${padded}=" ;;
  esac

  printf '%s' "$padded" | tr '_-' '/+' | base64 -d 2>/dev/null
}

ACCOUNT_ID=$(decode_jwt_payload "$GOMBOC_TOKEN" | jq -er '.tenantId // .accountId // empty' 2>/dev/null || true)
if [ -z "$ACCOUNT_ID" ]; then
  echo "Error: could not extract accountId from GOMBOC_TOKEN" >&2
  exit 1
fi

page=1
echo "[]" > "$OUTPUT"

while :; do
  resp=$(curl -sS "${BASE_URL}/api/v1/channels/search?page=${page}&perPage=${PER_PAGE}" \
    -H "Authorization: Bearer ${GOMBOC_TOKEN}")

  if [ "$(jq -r '.status' <<<"$resp")" != "success" ]; then
    echo "Error: $resp" >&2
    exit 1
  fi

  jq -s --arg accountId "$ACCOUNT_ID" \
    '.[0] + (.[1].data.channels | map(select(.accountId == $accountId)))' \
    "$OUTPUT" <(echo "$resp") > "${OUTPUT}.tmp"
  mv "${OUTPUT}.tmp" "$OUTPUT"

  count=$(jq -r '.data.channels | length' <<<"$resp")
  [ "$count" -lt "$PER_PAGE" ] && break
  page=$((page + 1))
done

echo "Exported $(jq 'length' "$OUTPUT") policy sets for account $ACCOUNT_ID to $OUTPUT"
```

#### Example output (`policy-sets.json`)

```json
[
  {
    "id": "a1b2c3d4-...",
    "accountId": "f0e1d2c3-...",
    "name": "default",
    "query": "",
    "filters": ["provider:aws", "risk:high"],
    "annotations": { "env": "production" },
    "createdAt": "2025-01-10T14:23:00.000Z",
    "updatedAt": "2025-04-03T09:01:00.000Z"
  },
  {
    "id": "e5f6a7b8-...",
    "accountId": "f0e1d2c3-...",
    "name": "cis-aws-hardening",
    "query": "classification:cis-aws",
    "filters": [],
    "annotations": { "framework": "CIS", "env": "all" },
    "createdAt": "2025-02-18T11:00:00.000Z",
    "updatedAt": "2025-05-01T16:30:00.000Z"
  }
]
```

***

### Restoring Policy Sets

Use `POST /api/v1/channels/batch/upsert` to restore from a backup file. This endpoint **creates or updates** up to 200 channels in a single request.

> **Note:** Only `name`, `query`, `filters`, and `annotations` are writable. The `id`, `accountId`, `createdAt`, and `updatedAt` fields are read-only and are stripped automatically by the script below.

#### Shell restore script

```bash
#!/usr/bin/env bash
# restore-policy-sets.sh — restores policy sets from a backup file
set -euo pipefail

BASE_URL="https://rules.app.gomboc.ai"
INPUT="${1:-policy-sets.json}"

payload=$(jq '{channels: [.[] | {name, query, filters, annotations}]}' "$INPUT")

curl -sS -X POST "${BASE_URL}/api/v1/channels/batch/upsert" \
  -H "Authorization: Bearer ${GOMBOC_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "$payload" | jq .
```

If the backup file contains more than 200 policy sets, split it into chunks of 200 and call the script once per chunk.

***

### Version Control with Git

Combine the export script with a Git repository to maintain a full history of every change to your policy configuration.

#### Recommended repository layout

```
policy-config/
├── policy-sets.json      ← exported channels (policy sets)
├── export.sh             ← export script
├── restore.sh            ← restore script
└── .github/
    └── workflows/
        └── backup.yml    ← automated daily backup
```

#### GitHub Actions: automated daily backup

```yaml
name: Backup Gomboc Policy Sets

on:
  schedule:
    - cron: "0 3 * * *"   # every day at 03:00 UTC
  workflow_dispatch:

permissions:
  contents: write

jobs:
  backup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install jq
        run: sudo apt-get update && sudo apt-get install -y jq

      - name: Export policy sets
        env:
          GOMBOC_TOKEN: ${{ secrets.GOMBOC_TOKEN }}
        run: bash export.sh

      - name: Commit if changed
        run: |
          git config user.name  "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add policy-sets.json
          if git diff --cached --quiet; then
            echo "No changes — policy sets are up to date."
          else
            git commit -m "chore: update policy sets backup $(date -u +%Y-%m-%d)"
            git push
          fi
```

Add `GOMBOC_TOKEN` as a repository secret in your GitHub repository settings.

***

### Restoring to a Different Environment

You can use the same backup file to copy policy sets across Gomboc organizations (e.g., from staging to production):

```bash
export GOMBOC_TOKEN="<production-org-token>"
bash restore.sh policy-sets.json
```

***

### API Quick Reference

All endpoints are on `https://rules.app.gomboc.ai`. Authentication: `Authorization: Bearer <PAT>`.

| Operation                  | Method   | Path                            | Key parameters                            |
| -------------------------- | -------- | ------------------------------- | ----------------------------------------- |
| List all policy sets       | `GET`    | `/api/v1/channels/search`       | `page`, `perPage`, `query`                |
| Get one policy set         | `GET`    | `/api/v1/channels/get`          | `name` (query param)                      |
| Create a policy set        | `POST`   | `/api/v1/channels/create`       | `name`, `query`, `filters`, `annotations` |
| Update a policy set        | `PUT`    | `/api/v1/channels/update`       | `name`, `query`, `filters`, `annotations` |
| Batch create               | `POST`   | `/api/v1/channels/batch/create` | array of channels, `onConflict`           |
| Batch upsert (restore)     | `POST`   | `/api/v1/channels/batch/upsert` | array of channels                         |
| Delete a policy set        | `DELETE` | `/api/v1/channels/delete`       | `name` (query param)                      |
| List rules in a policy set | `GET`    | `/api/v1/channels/rules`        | `name`, `page`, `perPage`                 |

For the full OpenAPI specification, see the Rule Service API reference.

***

### FAQ

**Will restoring a policy set overwrite the existing one?** Yes. `batch/upsert` updates existing channels matched by `name` and creates any that do not exist yet. To add new channels without touching existing ones, use `batch/create` with `"onConflict": "skip"` instead.

**Do I need to back up the rules themselves?** Built-in Gomboc rules are managed by Gomboc and do not need to be backed up. If you have published custom ORL rules, those can be exported separately using the `/api/v1/rules/search` endpoint — see the Rule Service API documentation.

**What happens to workspaces if I delete and re-create a policy set?** Workspace assignments are stored separately. Deleting and re-creating a policy set with the same `name` may require you to re-assign it to workspaces in the Portal. Use `batch/upsert` (update-in-place) to avoid this.
