Skip to content

Incident Response Playbook — BatleHub

This playbook covers security and availability incidents for BatleHub deployments. Follow the phases in order.

Guidance, not a commitment

A template for the playbook you write, not a service the project runs. The severity levels, response times and notification recipients below are examples — replace them with your organisation's, because the only people who can meet them are yours. Nobody is paged by this page.


Severity Levels

LevelDefinitionResponse SLA
P0 — CriticalData breach, token compromise, registry unavailable >15 minRespond immediately (24/7)
P1 — HighUnauthorized access to admin endpoints, persistent DoSWithin 2 hours
P2 — MediumAnomalous access patterns, failed-but-caught attackWithin 8 hours
P3 — LowPolicy misconfiguration, expired token alertsNext business day

Phase 1 — Detection

Automated signals

SourceAlertWhere to check
PrometheusBatleHubDown, BatleHubHighErrorRate, BatleHubHighDenyRatedeploy/prometheus-alerts.yaml
Audit logSpike in denied outcomes, unknown user IDs in user_id columnGET /api/v1/admin/audit-log
Rate limiterSustained 429 responses from one IPAudit log filter by IP
Container scanTrivy HIGH/CRITICAL finding in deployed image.github/workflows/image-scan.yaml
GitleaksSecret exposed in commit.github/workflows/secret-scan.yaml

Manual detection

Query the audit log for anomalies:

bash
# All denied events in the last hour
batlehub admin audit-log --denied-only --from "$(date -u -d '1 hour ago' +%FT%TZ)"

# Access events from a specific IP
batlehub admin audit-log | jq '[.[] | select(.ip_address == "1.2.3.4")]'

# What disappeared, and whether a person or a policy took it
batlehub admin audit-log --action delete,retention_reclaim --from <start>

# Export a full 24-hour window for offline analysis
batlehub admin export-audit-log --from <start> --to <end> --format csv --output incident-$(date +%Y%m%d).csv

Phase 2 — Containment

Act within the first 15 minutes for P0/P1. Do not wait for root-cause analysis before containing.

Block a suspicious IP

bash
batlehub admin ip-blocks add 1.2.3.4

Or via the Admin UI → IP Blocks → Add.

Revoke a compromised token

bash
batlehub admin token revoke <token-id>

Or via the Admin UI → Users → select user → Revoke token.

Block a user account

bash
batlehub admin users block <user-id>

Blocks all future requests from that user ID until unblocked.

Quarantine a malicious package

bash
# Block the package name across all versions
batlehub admin block <registry> <package-name>

# Or target a specific version
batlehub admin packages unlist <registry> <package-name> <version>

Isolate a replica

If one replica is compromised, remove it from the load balancer before forensics. BatleHub state lives in Postgres and S3 — the replica itself is stateless.


Phase 3 — Eradication

  1. Rotate credentials — generate new API tokens for all service accounts; update downstream consumers.
  2. Patch the vulnerability — if a CVE triggered the incident, apply the patch, run cargo audit locally, and rebuild the image.
  3. Re-run SBOM scantask security to confirm the patched dependency tree is clean.
  4. Review RBAC rules — use the RBAC simulator (POST /api/v1/admin/access-check) to validate that the affected policy gap is closed.

Phase 4 — Recovery

  1. Deploy patched image — run cargo build --release or trigger CI; push patched container.
  2. Verify healthGET /api/v1/health returns 200 on all replicas.
  3. Unblock legitimate traffic — remove IP blocks and unblock users that were collateral.
  4. Monitor — watch Prometheus for 30 minutes after restoration; confirm error rate returns to baseline.

Phase 5 — Post-Mortem

Within 5 business days of a P0/P1 incident:

  1. Write a timeline (detection → containment → eradication → recovery).
  2. Identify root cause and contributing factors.
  3. List corrective actions with owners and due dates.
  4. Update this playbook if any step was missing or unclear.
  5. Archive the exported audit log for the incident period (export-audit-log --format csv).

Post-mortem template: none yet. Write the first one against the five points above and keep it under operations/ so the next incident starts from a form rather than from a blank page.


PII Handling

Audit log entries contain user IDs and IP addresses. If a GDPR/CCPA deletion request arrives:

  1. Identify the user's ID from their account.
  2. Export their records: export-audit-log | jq '[.[] | select(.user_id == "X")]'
  3. Provide a copy to the user if required by your jurisdiction.
  4. To purge from the database, run the anonymization migration (planned feature) or a targeted UPDATE access_events SET user_id = 'anonymized', ip_address = NULL WHERE user_id = 'X' with DBA oversight.

Contacts

Populate these before deploying to production:

RoleContact
On-call engineer
Security lead
Legal / DPO
Upstream registry contacts (GitHub, npm, PyPI)

Released under the Apache 2.0 License. Made with ❤️ and too much ☕.