# Config Change Tracker

**Level:** 1 hour · a change history for every router

**APIs used:** GET /api/v2/configuration_managers/ (expand=router, synched, suspended)

---
Read https://cradlepoint.ericsson.com/llms.txt and the OpenAPI document at
https://cradlepoint.ericsson.com/developer/api/openapi.json so you know the
Ericsson NetCloud Manager (NCM) API.

Build me a Python script (standard library plus the git CLI) that snapshots the
NCM-managed configuration of every router in my account into a local git
repository, one JSON file per router, and commits only when something changed,
so I can `git log -p` config changes over time. Take the repository path as a
command-line argument, and create the repository if it doesn't exist.

Background you need:
- GET /api/v2/configuration_managers/ returns one record per router. Its
  `target` field is the configuration NCM manages for that router, as a
  two-element list [updates, removals]. These are the settings changed from
  the device defaults through NCM, not the router's full running config. A
  router with nothing managed shows [{}, []]. For managed routers, target
  includes ecm.config_version, a counter that goes up with every NCM change.
- Changes someone makes locally on the device do not appear here. The API
  reference describes this endpoint as where "config drift is detected and
  corrected", but don't describe this script as detecting local drift.
- `synched` is false when NCM's target hasn't been applied to the device yet.
  `suspended` is true when config sync is paused for that router.

Requirements:
- Fetch everything in one paged pass with
  GET /api/v2/configuration_managers/?expand=router&limit=500, so each record
  carries its router's name and ID. Don't make one call per router. Finish
  fetching every page before writing anything. If any request fails, or the
  API returns no records at all, exit non-zero without touching the
  repository. Skip any record whose router is null, with a warning.
- Snapshot the `target` field. Write stable output: sorted keys, 2-space
  indent, trailing newline. Name each file <router-id>.json, so renaming a
  router doesn't break its history. Also write INDEX.md, a table of router ID,
  name and file, sorted by name. Delete files for routers that no longer exist.
- Redact secrets before anything touches disk:
  - A key is sensitive if its name contains password, passphrase, psk,
    secret, token or community, or is exactly "key" or ends in "_key"
    (case-insensitive).
  - Under a sensitive key, replace every string and number value with
    "<redacted>", including values nested in dicts and lists below it. Keep
    the structure, and leave null and booleans as they are, so diffs still show
    a setting being cleared or toggled.
  - In any string, replace each PEM block (-----BEGIN ... -----END ...-----)
    with "<redacted>".
  - Never drop a key, so redaction never looks like a deleted setting in the
    diff.
- After the snapshot, print routers where synched is false ("NCM changes not
  yet applied") and where suspended is true ("sync paused").
- Stage only the files this script writes. Commit only if they changed; never
  create empty commits. Commit as
  "NCM Snapshot <ncm-snapshot@localhost>", so it works under cron without git
  config.
- v2 paths keep the trailing slash. Auth is four headers (X-CP-API-ID,
  X-CP-API-KEY, X-ECM-API-ID, X-ECM-API-KEY) read from the environment
  variables X_CP_API_ID, X_CP_API_KEY, X_ECM_API_ID and X_ECM_API_KEY. Send a
  descriptive User-Agent header.
- Follow meta.next exactly as returned until it is null. It may point at a
  regional host such as www.us0.cradlepointecm.com. Send the same headers
  there, but only ever send credentials to https:// URLs on cradlepointecm.com
  or its subdomains.
- Don't follow redirects automatically; treat any 3xx response as an error (a
  301 usually means a missing trailing slash). HTTP clients re-send custom
  headers to wherever a redirect points, which would leak your keys.
- On HTTP 429, wait until X-RateLimit-Reset (a Unix epoch) if that header is
  present, otherwise back off exponentially (1s, 2s, 4s, 8s). Make at most
  5 attempts per request in total, and never wait more than 60s at a time.
  On any other HTTP error, stop with a clear message. Use a 30s request timeout.

This script must be strictly read-only against NCM: GET requests only. Never
PUT, PATCH, POST or DELETE.

Then give me a crontab line to run it nightly. Cron doesn't inherit my shell
environment, so source the variables from a file only I can read (chmod 600).
