Scheduled Rotation Policies#

The RotationScheduler runs as a background asyncio task and automatically rotates infrastructure based on configurable policies. It uses the same Terraform-based deploy pipeline as the manual infraguard rotate command.

Policy Types#

TypeTriggerUse Case
scheduleFixed time intervalRotate every N hours regardless of burn state
on_burn_detectedBurnDetector firesRotate when a domain is flagged as burned
on_thresholdRequest countRotate after N requests hit a domain (limits exposure)
staggerTime interval, one domain at a timeRoll through all domains sequentially with a delay between each

Configuration#

rotation:
  enabled: true
  check_interval_seconds: 60
  work_dir_base: "/opt/infraguard/deployments"

  policies:
    - name: "nightly-cycle"
      type: "schedule"
      enabled: true
      interval_hours: 24
      provider: "do"
      region: "nyc1"
      ssh_key: "/root/.ssh/id_ed25519.pub"
      operator_ip: "1.2.3.4/32"

    - name: "burn-response"
      type: "on_burn_detected"
      enabled: true
      burn_cooldown_minutes: 5
      provider: "aws"
      ssh_key: "/root/.ssh/id_ed25519.pub"
      operator_ip: "1.2.3.4/32"

    - name: "request-cap"
      type: "on_threshold"
      enabled: true
      request_threshold: 10000
      threshold_window_seconds: 86400
      provider: "do"
      ssh_key: "/root/.ssh/id_ed25519.pub"
      operator_ip: "1.2.3.4/32"

    - name: "rolling-stagger"
      type: "stagger"
      enabled: true
      interval_hours: 48
      stagger_delay_minutes: 30
      domains:
        - "cdn.example.com"
        - "static.example.com"
        - "api.example.com"
      provider: "do"
      ssh_key: "/root/.ssh/id_ed25519.pub"
      operator_ip: "1.2.3.4/32"

How Each Policy Works#

Schedule#

Straightforward timer. The scheduler checks last_rotation and fires when interval_hours has elapsed. On first run, it records the current time as baseline and waits for the full interval before rotating.

On Burn Detected#

Reacts to the BurnDetector calling scheduler.notify_burn_detected(domain). The burn_cooldown_minutes setting adds a delay between detection and rotation so the operator has time to assess. If the burn alert is a false positive, the operator can clear it before rotation kicks in.

On Threshold#

Queries the tracking database for request count per domain within a rolling window. When any domain exceeds the threshold, a rotation fires. The window resets after each rotation.

Stagger#

Like schedule, but instead of rotating all domains at once, it queues them and rotates one at a time with stagger_delay_minutes between each. The first domain in the queue rotates immediately when the interval expires; subsequent domains rotate after the delay.

This avoids the blast radius of rotating everything simultaneously - if a rotation fails, only one domain is affected and the rest of the queue pauses.

CLI#

# List configured policies
infraguard schedule list

# Add a new policy
infraguard schedule add --name "every-12h" --type schedule --interval-hours 12 --provider do

# Remove a policy
infraguard schedule remove --name "every-12h"

Safety#

  • A minimum 120-second gap is enforced between any two rotations, regardless of policy.
  • Policies that are mid-rotation are skipped on subsequent ticks (no re-entry).
  • Rotation runs in a thread executor so it doesn’t block the asyncio event loop.
  • Rotation events are dispatched through the plugin pipeline so Discord/Slack get notified.

Rotation History#

All rotation outcomes (success, failure, rollback) are recorded in-memory on the scheduler. The API exposes them:

curl -H "Authorization: Bearer $TOKEN" https://ig:8080/api/rotation/history