Skip to content
Flagship Case Study

GitOps State Capture & CI-Enforced Secrets Control

Versioned, auditable infrastructure state for a mixed Windows/Linux fleet, protected by two independent secrets controls — pre-commit sanitization and a CI gate that blocks the merge. Zero credential exposure since deployment.

Situation

Configuration state for a four-node fleet lived only on the nodes themselves: no central capture, no change history, and no control preventing a credential from landing in version control. Losing a node meant rebuilding it from memory — an unbounded recovery time and an unauditable one.

Constraints: mixed operating systems (Windows and Linux), no budget for an enterprise backup product, GitHub free tier only, and a hard requirement that a credential can never be exposed even when the operator makes a mistake.

Task

  • Capture every node's configuration and service state as code on a daily cadence, with a full audit trail.
  • Sanitize secrets before commit and enforce the same rule in CI so nothing that slips the local filter can merge.
  • Apply one policy across Windows and Linux with platform-native tooling.
  • Deliver a tested rebuild path with a bounded recovery time objective.

Action

Each Node (daily timer) → Collect State → Sanitize Secrets → Git Commit
                                                       ↓
                              Git Push → GitHub Actions CI Gate
                                            ↓           ↓
                                      ✅ Merge    🚫 Block + Alert

Defense in depth: the sanitizer strips secrets before commit, and the CI gate independently rejects anything that slips through.

  • Linux collection (Nova, Mira, Orin). systemd user timers run a full state capture daily at 02:30 plus an hourly delta sync; Bash collectors gather system and service state.
  • Windows collection (Jasper). PowerShell collectors capture OS, GPU, and service state via Task Scheduler on the same daily cadence — different tooling, identical policy.
  • Pre-commit sanitization. Regex-based stripping of API keys, tokens, private keys, emails, and phone numbers on both platforms.
  • CI secret-scanning gate. A GitHub Actions workflow scans every push against 11 secret patterns and a filename blocklist (.env, auth-profiles.json) and blocks the merge on any match.
  • Segregated repository layout. Each node owns its directory, so a compromised or misbehaving node cannot overwrite another's state.
  • Runbooks. Credential policy documentation, an incident-response runbook for a suspected leak, and a tested node-rebuild procedure.
Implementation
Linux Nodes (Nova/Mira/Orin)
  • lab-backup.sh — Collect system + OpenClaw state
  • lab-sanitize.sh — Regex-based secret stripping
  • lab-git-push.sh — Lightweight hourly sync
  • install_timer.sh — systemd user timer setup
  • Daily full backup at 02:30 + hourly delta sync
Windows Node (Jasper)
  • LabBackup.ps1 — Collect OS, GPU, OpenClaw state
  • Sanitize.ps1 — PowerShell secret stripping
  • SetupScheduledTask.ps1 — Task Scheduler setup
  • Daily backup at 02:30 AM
CI Gate
  • verify-no-secrets.yml — GitHub Actions workflow
  • 11 regex patterns (API keys, tokens, private keys, etc.)
  • Filename blocklist (.env, auth-profiles.json)

Result

0
Credential exposures
11
Secret patterns enforced
30m
Node rebuild (RTO)
24h
Max data loss (RPO)
  • Recovery: clone the repository, follow the runbook, and a node is rebuilt in 30 minutes — a bounded, tested RTO instead of an unknown.
  • Security: zero credential exposures since deployment, with two independent controls between an operator mistake and the remote.
  • Auditability: every configuration change across 4 nodes is tracked in Git with timestamps and diffs.
  • Consistency: one policy enforced identically on Windows and Linux.
  • Self-documenting: each capture includes system specs, services, and health data, so the repository doubles as live infrastructure documentation.

Lessons & Next Steps

  • Defense in depth for secrets. One layer is not enough. The sanitizer catches known patterns locally; the CI gate catches anything that evolves past the local filter.
  • Different tooling, one policy. PowerShell and Bash implementations look different but enforce identical capture and sanitization rules.
  • Untested backups are not backups. The restore runbook is validated by actually rebuilding nodes from the repository.
  • Git is a strong state store. Versioned, diffable, hosted off-site, and with CI hooks built in.
Next Steps
  • Add automated restore testing (monthly dry-run rebuild)
  • Expand to backup VM snapshots metadata from Proxmox
  • Add Slack/Telegram notification on backup failure
GitHub Actions CI run showing secret scanning results — 11 patterns checked, zero secrets detected across 4 nodes

Redacted sample (structure accurate; sensitive values removed).

What to look at: Secret-scan CI gate with 11 pattern checks, per-node backup status with timestamps, and restore readiness indicator.

Evidence: Secret Scanning CI Gate (sanitized)

# .github/workflows/verify-no-secrets.yml (excerpt)
- name: Scan for secrets
  run: |
    PATTERNS=(
      'AKIA[0-9A-Z]{16}'           # AWS Access Key
      'ghp_[a-zA-Z0-9]{36}'        # GitHub PAT
      '-----BEGIN.*PRIVATE KEY-----' # Private keys
      'sk-[a-zA-Z0-9]{48}'         # OpenAI API key
      'token.*=.*[a-f0-9]{32,}'    # Generic tokens
    )
    # 11 patterns total — blocks push on match
4
Nodes backed up
11
Patterns blocked
0
Credential exposures
24h
Max data loss
30m
Node rebuild time

Related artifacts: Proof Pack →