# Rule Keeper sync CLI: step-by-step manual `rule-keeper-sync` is a small command-line tool that keeps Rule Keeper up to date without pasting exports by hand. It runs on **your** computer or CI runner, downloads your Jira Automation rules through Atlassian's public Automation REST API using **your** API token, normalises them exactly as the app does, and pushes a signed snapshot to the app. What this means for security and compliance: - Your API token never leaves the machine that runs the CLI. The app does not store any credential for your Jira site. - The app makes no outbound network calls. It stays eligible for Atlassian's **Runs on Atlassian** badge. - The app accepts a snapshot only when it is signed (HMAC-SHA256) with a secret you generate in the app. You can rotate or revoke that secret at any time. - Only a compact summary of each rule is stored: name, state, scope, trigger type, counts of components and a fingerprint of the logic. Rule bodies, smart values and comments are not stored. This manual is written for a Jira administrator who has never used a command line. Every step shows exactly what to type and what to expect. --- ## 1. Prerequisites | What | Why | How to check | | --- | --- | --- | | Jira administrator rights on the site | The Automation API only returns rules to admins | Jira → Settings (cog) → System opens without an error | | Node.js 22 or newer | Runs the CLI (one file, no other dependencies) | In a terminal: `node -v` prints `v22…` or higher | | The file `rule-keeper-sync.mjs` | The CLI itself | Provided by NoxDevelopment with the app, see section 3 | | An Atlassian API token | Authenticates the CLI against Atlassian | Created in section 4 | | A Rule Keeper sync secret | Authenticates the CLI against the app | Created in section 5 | ### 1.1 Install Node.js (if `node -v` fails) 1. Open and download the **LTS** installer for your operating system (Windows `.msi`, macOS `.pkg`). 2. Run the installer with default options. 3. Close every terminal window and open a new one (PowerShell on Windows, Terminal on macOS). 4. Type `node -v` and press Enter. You should see a version starting with `v22` or higher. If you see "command not found", restart the computer once and try again. Alternative on Windows: `winget install OpenJS.NodeJS.LTS`. --- ## 2. Find the app in Jira 1. In Jira, click the **cog icon** (top right) → **Apps**. 2. In the left sidebar, under the **Apps** heading, click **Rule Keeper**. 3. The page has six tabs: Cost, Inventory, Changes, Lint, Upload, Sync. Everything in this manual happens in the **Sync** tab. Polish UI: Ustawienia → Aplikacje, then the **Aplikacje** heading in the left sidebar → Rule Keeper. The tabs are Koszty, Inwentarz, Zmiany, Lint, Wgraj, Synchronizacja. Direct link pattern (replace the placeholders): ``` https://.atlassian.net/jira/settings/apps// ``` The exact link is shown in the browser address bar when you open the page. --- ## 3. Get the CLI file 1. Download `rule-keeper-sync.mjs` and `README.md` (this manual) from the latest release: https://noxdevelopment.pages.dev/rule-keeper/downloads/ 2. Create a folder for it, for example: - Windows: `C:\Tools\rule-keeper\` - macOS / Linux: `~/tools/rule-keeper/` 3. Copy both files into that folder. 4. Open a terminal **in that folder**: - Windows: open the folder in Explorer, click the address bar, type `powershell`, press Enter. - macOS: right-click the folder → Services → New Terminal at Folder, or `cd ~/tools/rule-keeper`. 5. Verify the file runs: ```bash node rule-keeper-sync.mjs --help ``` You should see a usage text starting with `rule-keeper-sync: push your Jira Automation rules to Rule Keeper`. --- ## 4. Create an Atlassian API token 1. Open while logged in as the admin account you want the CLI to use. 2. Click **Create API token** (the classic, unscoped token). Do **not** choose "Create API token with scopes": scoped tokens do not cover the Automation API and the CLI will get HTTP 403. 3. Label: `rule-keeper-sync`. Expiry: choose according to your policy; note the date, the CLI will start failing with HTTP 401 when the token expires. 4. Click **Create**, then **Copy**. 5. Paste the token into your password manager **now**. Atlassian will not show it again. --- ## 5. Generate the Rule Keeper sync secret 1. In the **Sync** tab click **Generate secret**. 2. A yellow box appears with the secret (starts with `rks_`). Click into the box, select all, copy. 3. Paste it into your password manager next to the API token. 4. The box disappears when you leave the page. The app keeps only an encrypted copy and cannot show the secret again. If you lose it, click **Rotate secret** and use the new one. Below the buttons the tab shows **Your web trigger URL**. Copy it as well; you will pass it to the CLI with `--url`. --- ## 6. First run (dry run) A dry run downloads and normalises your rules but pushes nothing. It confirms that the token and the site are right before anything reaches the app. ### Windows (PowerShell) ```powershell $env:ATLASSIAN_API_TOKEN = "" node rule-keeper-sync.mjs --site .atlassian.net --email --dry-run --out rules.json ``` ### macOS / Linux (bash or zsh) ```bash export ATLASSIAN_API_TOKEN="" node rule-keeper-sync.mjs --site .atlassian.net --email --dry-run --out rules.json ``` Expected output (numbers will differ): ``` [rule-keeper-sync] site acme.atlassian.net, account admin@acme.example [rule-keeper-sync] cloud id: 3f1c… [rule-keeper-sync] page 1: 100 rule summaries (total 100) [rule-keeper-sync] page 2: 82 rule summaries (total 182) [rule-keeper-sync] fetched 25/182 rules … [rule-keeper-sync] fetched 182/182 rules [rule-keeper-sync] normalised 182 rules (0 parser notes) [rule-keeper-sync] wrote export-style file rules.json dry run: 182 rules normalised, written to rules.json ``` `rules.json` has the same shape as Jira's own "Export rules" file, so you can paste it into the **Upload** tab if you ever prefer a manual snapshot. Notes: - `--site` is the host name only, without `https://`. - Secrets are read from environment variables only. Never pass them as command-line arguments; they would end up in shell history. - If the account is not a Jira admin the API returns zero rules or HTTP 403. --- ## 7. First real sync ### Windows (PowerShell) ```powershell $env:ATLASSIAN_API_TOKEN = "" $env:RULE_KEEPER_SYNC_SECRET = "" node rule-keeper-sync.mjs --site .atlassian.net --email --url ``` ### macOS / Linux ```bash export ATLASSIAN_API_TOKEN="" export RULE_KEEPER_SYNC_SECRET="" node rule-keeper-sync.mjs --site .atlassian.net --email --url ``` Expected last lines: ``` [rule-keeper-sync] pushing 54 KB to Rule Keeper [rule-keeper-sync] snapshot accepted done: 182 rules pushed to Rule Keeper ``` Then reload the Rule Keeper page: - the green banner shows the new snapshot with **source: sync CLI**; - the Sync tab status box shows **Sync active** with the time of the last accepted sync and the rule count; - after the second sync the **Changes** tab compares the two snapshots. --- ## 8. Schedule it Each run creates one snapshot. The app keeps the 10 most recent snapshots. A daily run is a sensible default; hourly is fine for small sites. ### 8.1 Windows Task Scheduler 1. Create a file `C:\Tools\rule-keeper\sync.ps1`: ```powershell $env:ATLASSIAN_API_TOKEN = (Get-Content "C:\Tools\rule-keeper\token.txt" -Raw).Trim() $env:RULE_KEEPER_SYNC_SECRET = (Get-Content "C:\Tools\rule-keeper\secret.txt" -Raw).Trim() node "C:\Tools\rule-keeper\rule-keeper-sync.mjs" --site .atlassian.net --email --url --quiet exit $LASTEXITCODE ``` 2. Save the token and the secret in `token.txt` and `secret.txt` in that folder. Restrict the folder to your Windows account (Properties → Security) so other users cannot read it. 3. Open **Task Scheduler** → **Create Task**. - General: name `Rule Keeper sync`, "Run whether user is logged on or not". - Triggers: New → Daily, pick a time. - Actions: New → Program `powershell.exe`, arguments `-NoProfile -ExecutionPolicy Bypass -File "C:\Tools\rule-keeper\sync.ps1"`. - Settings: tick "Run task as soon as possible after a scheduled start is missed". 4. Right-click the task → **Run** once and check the Sync tab in Jira. ### 8.2 cron (macOS, Linux) 1. Create `~/tools/rule-keeper/sync.sh`: ```bash #!/usr/bin/env bash set -euo pipefail export ATLASSIAN_API_TOKEN="$(cat "$HOME/tools/rule-keeper/token.txt")" export RULE_KEEPER_SYNC_SECRET="$(cat "$HOME/tools/rule-keeper/secret.txt")" node "$HOME/tools/rule-keeper/rule-keeper-sync.mjs" --site .atlassian.net --email --url --quiet ``` 2. `chmod 700 sync.sh token.txt secret.txt` 3. `crontab -e` and add a daily run at 06:15: ``` 15 6 * * * /home//tools/rule-keeper/sync.sh >> /home//tools/rule-keeper/sync.log 2>&1 ``` ### 8.3 GitHub Actions 1. In a repository you control, add the secrets `ATLASSIAN_API_TOKEN` and `RULE_KEEPER_SYNC_SECRET` (Settings → Secrets and variables → Actions). 2. Commit `rule-keeper-sync.mjs` to the repository. 3. Add `.github/workflows/rule-keeper-sync.yml`: ```yaml name: rule-keeper-sync on: schedule: - cron: "15 6 * * *" workflow_dispatch: jobs: sync: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - run: node rule-keeper-sync.mjs --site .atlassian.net --email --url --quiet env: ATLASSIAN_API_TOKEN: ${{ secrets.ATLASSIAN_API_TOKEN }} RULE_KEEPER_SYNC_SECRET: ${{ secrets.RULE_KEEPER_SYNC_SECRET }} ``` 4. Run it once from the Actions tab (workflow_dispatch) and check the Sync tab in Jira. --- ## 9. Rotating or revoking the secret - **Rotate** (Sync tab → Rotate secret): the old secret stops working immediately. Update `RULE_KEEPER_SYNC_SECRET` wherever the command runs. Do this when a person who knew the secret leaves, or on your regular rotation schedule. - **Revoke** (Sync tab → Revoke secret): the app rejects every sync until a new secret is generated. Existing snapshots stay available. - Rotating the **Atlassian API token** does not involve the app: create a new token (section 4), replace `ATLASSIAN_API_TOKEN`, revoke the old token at id.atlassian.com. --- ## 10. Reference ### Command-line options | Option | Environment variable | Meaning | | --- | --- | --- | | `--site ` | `RK_SITE` | Jira site, e.g. `acme.atlassian.net` | | `--email ` | `RK_EMAIL` | Atlassian account e-mail | | `--url ` | `RK_SYNC_URL` | Web trigger URL from the Sync tab | | `--cloud-id ` | `RK_CLOUD_ID` | Skip the tenant lookup (optional) | | (none) | `ATLASSIAN_API_TOKEN` | API token, required | | (none) | `RULE_KEEPER_SYNC_SECRET` | App secret, required unless `--dry-run` | | `--concurrency ` | | Parallel rule downloads, 1 to 16, default 4 | | `--dry-run` | | Download and normalise only | | `--out ` | | Write an export-style JSON file | | `--json` | | Print the summary as JSON on stdout | | `--quiet` | | No progress lines on stderr | | `--help` | | Usage | ### Exit codes | Code | Meaning | What to do | | --- | --- | --- | | 0 | Success | Nothing | | 2 | Configuration error | Read the message; it lists the missing flag or variable | | 3 | Atlassian API error | See the HTTP status below | | 4 | Rejected by the app | See the app status below | | 5 | Unexpected error | Re-run with the full output and contact support | ### Atlassian API HTTP statuses (exit code 3) | Status | Cause | Fix | | --- | --- | --- | | 401 | Wrong e-mail or token, or expired token | Create a new token (section 4) | | 403 | Account is not a Jira admin on this site, or scoped token | Use an admin account and a classic token | | 404 | Site name wrong or site has no Automation | Check `--site` | | 429 | Rate limit | The CLI retries automatically; lower `--concurrency` for very large sites | ### App HTTP statuses (exit code 4) | Status | Cause | Fix | | --- | --- | --- | | 401 | Wrong secret, clock skew above 5 minutes, or the same request sent twice | Check `RULE_KEEPER_SYNC_SECRET`; sync the computer clock | | 403 | No secret configured in the app | Generate one in the Sync tab | | 400 | Protocol mismatch | Update the CLI file | | 413 | Snapshot above 4 MB | Contact support | | 405 | URL is not the web trigger | Copy the URL from the Sync tab again | | 402 | The Rule Keeper license on this site is not active | Renew the subscription in Jira settings → Apps → Manage apps | ### What is sent to the app One JSON document per run, signed with HMAC-SHA256: ```json { "version": 1, "source": "SyncCliSource", "site": "acme.atlassian.net", "cloudId": "…", "generatedAt": "2026-10-10T21:00:00.000Z", "rules": [ { "id": "", "name": "…", "state": "ENABLED", "scope": { "type": "PROJECT", "projectIds": ["10010"] }, "trigger": { "type": "jira.issue.event.trigger:created", "label": "Issue created" }, "stats": { "actions": 2, "conditions": 1, "branches": 0, "depth": 2 }, "componentTypes": ["CONDITION:jira.issue.condition", "ACTION:jira.issue.edit"], "labels": [], "authorAccountId": "…", "actorAccountId": "…", "created": "…", "updated": "…", "notifyOnError": "FIRSTERROR", "writeAccessType": "UNRESTRICTED", "canOtherRuleTrigger": false, "usesConnections": false, "fingerprint": "653eed85" } ], "warnings": [] } ``` Headers: `X-RuleKeeper-Version: 1`, `X-RuleKeeper-Timestamp: `, `X-RuleKeeper-Signature: v1=.")>`.