BCPPlan.md

May 30, 2026 Β· View on GitHub

Hack23 Logo

πŸ”„ Riksdagsmonitor β€” Business Continuity Plan

πŸ›‘οΈ Dual-Deployment Resilience Framework
🎯 Enterprise-Grade Availability Through Geographic Redundancy

Owner Version Effective Date Review Cycle

πŸ“‹ Document Owner: CEO | πŸ“„ Version: 1.3 | πŸ“… Last Updated: 2026-05-28 (UTC)
πŸ”„ Review Cycle: Quarterly | ⏰ Next Review: 2026-08-28
πŸ“Œ Classification: Public


🎯 Purpose Statement

Riksdagsmonitor's business continuity framework demonstrates how geographic redundancy and automated failover directly enable operational resilience and service availability. Our dual-deployment strategy serves as both operational necessity and technical demonstration of enterprise-grade reliability principles.

This plan is designed to maintain the riksdagsmonitor.com platform during infrastructure disruptions through AWS multi-region deployment (primary) and GitHub Pages disaster recovery (standby), targeting 99.998% availability under normal operating conditions, with CloudFront origin failover typically completing in under 60 seconds and DNS/Route 53 failover (including health checks and DNS propagation) completing within approximately 15 minutes during full-region incidents.

β€” James Pether SΓΆrling, CEO/Founder


πŸ“Š Business Impact Analysis

🎯 Service Availability Requirements

Riksdagsmonitor provides public political transparency services requiring high availability but tolerating brief disruptions:

%%{
  init: {
    "theme": "base",
    "themeVariables": {
      "primaryColor": "#1565C0",
      "primaryTextColor": "#0d47a1",
      "lineColor": "#1565C0",
      "secondaryColor": "#4CAF50",
      "tertiaryColor": "#FF9800"
    }
  }
}%%
graph TB
    subgraph BIA["πŸ“Š Business Impact Analysis"]
        FINANCIAL[πŸ’° Financial Impact<br/>No direct revenue loss]
        OPERATIONAL[βš™οΈ Operational Impact<br/>Service unavailable]
        REPUTATIONAL[🀝 Reputational Impact<br/>Public trust in transparency]
        CIVIC[πŸ›οΈ Civic Impact<br/>Democratic accountability]
    end
    
    subgraph RECOVERY["πŸ”„ Recovery Requirements"]
        RTO[⏰ RTO Target<br/>&lt; 30 seconds origin failover<br/>&lt; 15 minutes DNS failover]
        RPO[πŸ’Ύ RPO Target<br/>&lt; 15 minutes<br/>near-zero effective RPO (S3 replication lag)]
        AVAILABILITY[πŸ“ˆ Availability Target<br/>99.998%<br/>β‰ˆ10.5 minutes (~631 seconds) downtime/year]
    end
    
    subgraph DEPLOYMENT["🌍 Deployment Strategy"]
        PRIMARY[☁️ AWS Primary<br/>CloudFront + S3 Multi-Region]
        DR[πŸ“ GitHub Pages DR<br/>Standby Deployment]
        FAILOVER[πŸ”„ Automatic Failover<br/>Route 53 Health Checks]
    end
    
    FINANCIAL --> RTO
    OPERATIONAL --> RTO
    REPUTATIONAL --> RPO
    CIVIC --> AVAILABILITY
    
    RTO --> PRIMARY
    RPO --> PRIMARY
    AVAILABILITY --> PRIMARY
    
    PRIMARY --> FAILOVER
    DR --> FAILOVER
    
    style BIA fill:#1565C0,color:#ffffff
    style RECOVERY fill:#FF9800,color:#000000
    style DEPLOYMENT fill:#4CAF50,color:#000000

πŸ“ˆ Impact Thresholds

Service ComponentπŸ’° Financial Impactβš™οΈ Operational Impact🀝 Reputational ImpactπŸ›οΈ Civic Impact🎯 Recovery Priority
🌐 Static WebsiteMinimalHighHighCriticalπŸ”΄ Critical
πŸ“Š Content UpdatesMinimalModerateModerateModerate🟑 Medium
πŸ” Search IndexingMinimalLowLowLow🟒 Standard

πŸ—οΈ Infrastructure Architecture

🌍 Dual-Deployment Strategy

%%{
  init: {
    "theme": "base",
    "themeVariables": {
      "primaryColor": "#1565C0",
      "primaryTextColor": "#0d47a1",
      "lineColor": "#1565C0",
      "secondaryColor": "#4CAF50",
      "tertiaryColor": "#FF9800"
    }
  }
}%%
graph TB
    subgraph ROUTE53["🌐 Route 53 DNS"]
        DNS[πŸ“‘ DNS Service<br/>Health Checks Every 30s]
        HEALTHCHECK[βš•οΈ Health Checker<br/>Tests CloudFront Endpoint]
    end
    
    subgraph PRIMARY["☁️ AWS Primary (Active)"]
        CF[🌍 CloudFront CDN<br/>600+ PoPs<br/>Automatic Origin Failover]
        S3_US[πŸ’Ύ S3 us-east-1<br/>Primary Origin<br/>Versioning Enabled]
        S3_EU[πŸ’Ύ S3 eu-west-1<br/>Replica Origin<br/>Asynchronous Replication (&lt;15 min RPO)]
        
        CF -->|Primary| S3_US
        CF -->|Failover on 5xx errors| S3_EU
        S3_US -.->|Replication| S3_EU
    end
    
    subgraph DR["πŸ“ GitHub Pages (Standby)"]
        GH[πŸ“„ GitHub Pages<br/>Default branch (root)<br/>Automated Deployment]
    end
    
    USERS[πŸ‘₯ Users] -->|DNS Query| DNS
    HEALTHCHECK -->|Monitor| CF
    DNS -->|Healthy: Return CloudFront alias/hostname| USERS
    DNS -.->|3 Failed Checks (~90s detection)<br/>+ DNS TTL/propagation (up to ~15 min total)| USERS
    USERS -->|HTTPS/TLS 1.3| CF
    USERS -.->|HTTPS/TLS 1.3 (DR)| GH
    
    style ROUTE53 fill:#1565C0,color:#ffffff
    style PRIMARY fill:#4CAF50,color:#000000
    style DR fill:#FF9800,color:#000000

πŸ›‘οΈ Availability Objectives & Assumptions

These are business continuity design objectives, not contractual guarantees. Availability figures are based on underlying cloud provider SLAs and documented reliability targets.

ComponentProvider SLAFailover MechanismTarget RTOTarget RPONotes
🌍 CloudFront99.9% (AWS SLA)Origin failover< 30 secondsβ‰ˆ 0 minutesCache may serve slightly stale content during failover
πŸ’Ύ S3 us-east-199.99% (AWS SLA)Multi-region replica< 30 seconds< 15 minutesS3 cross-region replication typically completes within minutes; static content allows near-zero effective RPO
πŸ’Ύ S3 eu-west-199.99% (AWS SLA)Primary failback< 30 seconds< 15 minutesReplication lag possible; static content minimizes data loss impact
🌐 Route 53100% (AWS SLA)Health check failover (30s Γ— 3 checks)15 minutesβ‰ˆ 0 minutesIncludes health check detection (90s) + DNS TTL propagation (~14 min)
πŸ“ GitHub Pages99.9% (target; no formal SLA)Route 53 automated DNS failover15 minutesUp to last deploymentStatic content served via Route 53 health-check based DNS failover; RPO = time since last successful GitHub Actions deploy
🎯 CombinedDesign target β‰ˆ 99.998%Automated multi-layer< 30 seconds (objective)< 15 minutes for static content (objective)Theoretical calculation assuming largely independent failures

Disclaimer: These are business continuity design objectives based on AWS published SLAs (CloudFront 99.9%, S3 99.99%, Route 53 100%) and GitHub public reliability targets. The combined 99.998% availability is a theoretical design target assuming largely independent failures. Actual end-to-end availability may be lower in practice. RPO values reflect S3 cross-region replication characteristics (typically < 15 minutes) and static content deployment timing; actual RPO may vary.


🚨 Disaster Recovery Scenarios

Scenario 1: S3 us-east-1 Region Failure

RTO RPO Impact

πŸ” Detection:

  • CloudFront origin monitoring detects 500+ HTTP errors from us-east-1
  • Automatic failover triggered without manual intervention

πŸ”„ Recovery Procedure:

  1. ⚑ CloudFront automatically routes to S3 eu-west-1 origin (< 30 seconds)
  2. πŸ“Š Verify service availability via monitoring
  3. πŸ“ Log incident for post-event analysis
  4. ⏳ Monitor AWS status for us-east-1 restoration
  5. πŸ”™ Automatic failback when us-east-1 recovers

βœ… Validation:

  • Service availability confirmed via health checks
  • User experience unaffected (transparent failover)
  • Content served from eu-west-1 (identical to us-east-1)

Scenario 2: CloudFront Global Outage

RTO RPO Impact

πŸ” Detection:

  • Route 53 health checks fail for CloudFront endpoint
  • Automated DNS failover to GitHub Pages after health check detection + DNS propagation (β‰ˆ 15 minutes total)

πŸ”„ Recovery Procedure:

  1. βš•οΈ Route 53 detects CloudFront health check failures (30s intervals Γ— 3 failures = 90 seconds detection time)
  2. 🌐 DNS automatically updates riksdagsmonitor.com β†’ GitHub Pages
  3. πŸ“Š Verify GitHub Pages serving traffic
  4. πŸ“§ Notify CEO of failover event
  5. ⏳ Monitor CloudFront status for restoration
  6. πŸ”™ Intentionally manual DNS failback after CloudFront recovery and stability confirmation
    • Rationale: Failback is manual by design to avoid DNS flapping and ensure human verification before restoring CloudFront as primary

βœ… Validation:

  • GitHub Pages availability confirmed
  • Users redirected via DNS (up to 15-minute TTL)
  • Content identical (synchronized deployment)

Scenario 3: Both AWS S3 Regions Unavailable

RTO RPO Impact

πŸ” Detection:

  • CloudFront cannot reach either S3 origin
  • Route 53 health checks fail

πŸ”„ Recovery Procedure:

  1. ⚑ CloudFront attempts origin failover (< 30 seconds)
  2. 🌐 Route 53 DNS failover to GitHub Pages (15 minutes)
  3. πŸ“Š Verify GitHub Pages serving traffic
  4. πŸ“§ CEO notification of major AWS outage
  5. ⏳ Monitor AWS status dashboard
  6. πŸ”™ DNS failback after AWS recovery

βœ… Validation:

  • Service restored via GitHub Pages
  • Incident documented with AWS service disruption details

Scenario 4: AWS Account Compromise

RTO RPO Impact

πŸ” Detection:

  • CloudTrail alerts for unauthorized API calls
  • GuardDuty security findings
  • Unexpected configuration changes

πŸ”„ Recovery Procedure:

  1. πŸ”’ Immediate DNS failover to GitHub Pages (operator action: 2 minutes; client-visible cutover: up to DNS TTL propagation ~15 minutes)
  2. πŸ” Revoke all AWS IAM credentials and access keys
  3. πŸ”„ Update AWS IAM role trust policy for GitHub Actions OIDC provider to revoke compromised trust
  4. πŸ“Š CloudTrail audit of unauthorized actions
  5. πŸ›‘οΈ AWS Support engagement for forensics
  6. πŸ”§ Restore infrastructure from documented configuration and backups (future-state: Infrastructure-as-Code)
  7. βœ… Security validation before DNS failback

βœ… Validation:

  • Service operational on GitHub Pages
  • All compromised credentials revoked
  • Forensic analysis completed
  • Infrastructure hardened before restoration

Scenario 5: GitHub Pages Unavailable (During DR)

RTO RPO Impact

πŸ” Detection:

  • GitHub Pages deployment failure
  • Health checks fail for GitHub Pages endpoint

πŸ”„ Recovery Procedure:

  1. πŸ“Š Verify GitHub status dashboard
  2. 🌐 If AWS available, revert DNS to CloudFront immediately
  3. πŸ“„ If both unavailable, deploy to alternative CDN (Cloudflare Pages, Netlify)
  4. πŸ“¦ Build static site from Git main branch
  5. 🌐 Update DNS to alternative CDN
  6. πŸ”™ Restore to primary after AWS/GitHub recovery

βœ… Validation:

  • Alternative deployment confirmed operational
  • DNS propagation verified
  • Incident escalated to GitHub Support

πŸ“‹ Recovery Team Structure

🎯 Business Continuity Team

πŸ‘¨β€πŸ’Ό CEO (James Pether SΓΆrling) - Business Continuity Coordinator

  • πŸ”‘ Authority: Full decision-making power for continuity actions
  • 🎯 Responsibilities: Strategic decisions, stakeholder communication, recovery coordination
  • πŸ“ž Contact: Primary mobile, backup email, monitoring alerts
  • πŸ› οΈ Tools: AWS Console, GitHub CLI, Route 53 DNS management, CloudWatch

πŸ”§ Technical Recovery (CEO as Technical Lead)

  • 🎯 Responsibilities: AWS infrastructure, GitHub Pages, DNS failover, health check monitoring
  • πŸ› οΈ Tools: AWS Console, AWS CLI, GitHub Actions, Route 53, CloudWatch
  • πŸ“ž Escalation Paths: AWS Enterprise Support, GitHub Enterprise Support

πŸ“ž Emergency Contact Matrix

πŸ‘€ RoleπŸ“ž Primary ContactπŸ”„ Backup Method⏰ Response Time
πŸ‘¨β€πŸ’Ό CEO/CoordinatorπŸ“± Mobile phoneπŸ“§ Email + SMS< 15 minutes
☁️ AWS Support🌐 Enterprise PortalπŸ“ž Phone support< 15 minutes
πŸ“ GitHub Support🌐 Enterprise PortalπŸ“§ Email< 1 hour
🌐 Route 53 Operations☁️ AWS ConsoleπŸ“± Mobile app< 5 minutes
πŸ“Š Monitoring AlertsπŸ“§ Email + πŸ“± SMSπŸ’¬ Chat/IMReal-time

🚨 Emergency Activation

πŸ“ž Immediate Actions (First 15 Minutes)

  1. πŸ“Š Assess Situation: Determine scope via CloudWatch, Route 53 health checks
  2. πŸ” Identify Failure Point: AWS infrastructure, DNS, GitHub Pages
  3. πŸš€ Activate Recovery: Automatic (CloudFront failover) or manual (DNS update)
  4. πŸ“’ Log Incident: Document detection time, symptoms, actions taken
  5. πŸ“§ Stakeholder Notification: CEO notification via monitoring alerts

πŸ”„ Recovery Activation Decision Tree

%%{
  init: {
    "theme": "base",
    "themeVariables": {
      "primaryColor": "#1565C0",
      "primaryTextColor": "#0d47a1",
      "lineColor": "#1565C0",
      "secondaryColor": "#4CAF50",
      "tertiaryColor": "#FF9800"
    }
  }
}%%
graph TD
    INCIDENT[🚨 Service Disruption Detected] --> CHECK_CF{CloudFront<br/>Accessible?}
    
    CHECK_CF -->|No| MANUAL_DNS[🌐 Manual DNS Failover<br/>to GitHub Pages<br/>RTO: 2 minutes]
    CHECK_CF -->|Yes| CHECK_S3{S3 Origins<br/>Accessible?}
    
    CHECK_S3 -->|us-east-1 No| AUTO_FAILOVER[⚑ Automatic Origin Failover<br/>to eu-west-1<br/>RTO: &lt; 30 seconds]
    CHECK_S3 -->|Both No| ROUTE53_FAILOVER[βš•οΈ Route 53 Health Check<br/>DNS Failover<br/>RTO: 15 minutes]
    CHECK_S3 -->|Yes| CHECK_HEALTH{Health Check<br/>Passing?}
    
    CHECK_HEALTH -->|No| INVESTIGATE[πŸ” Investigate Root Cause<br/>Application Error?<br/>Configuration Issue?]
    CHECK_HEALTH -->|Yes| FALSE_ALARM[βœ… False Alarm<br/>Monitor and Document]
    
    MANUAL_DNS --> VERIFY[βœ… Verify Service Restored]
    AUTO_FAILOVER --> VERIFY
    ROUTE53_FAILOVER --> VERIFY
    INVESTIGATE --> VERIFY
    
    VERIFY --> DOCUMENT[πŸ“ Incident Documentation<br/>Post-Event Analysis]
    
    style INCIDENT fill:#FF9800,color:#000000
    style MANUAL_DNS fill:#1565C0,color:#ffffff
    style AUTO_FAILOVER fill:#4CAF50,color:#000000
    style ROUTE53_FAILOVER fill:#1565C0,color:#ffffff
    style VERIFY fill:#4CAF50,color:#000000

πŸ§ͺ Testing & Validation

πŸ“… BCP Testing Schedule

Test TypeFrequencyScopeSuccess Criteria
⚑ Origin Failover TestQuarterlyCloudFront β†’ S3 eu-west-1Failover < 30 seconds, no data loss
🌐 DNS Failover TestSemi-AnnualRoute 53 β†’ GitHub PagesFailover within 15 minutes, content identical
πŸ”™ Failback TestQuarterlyReturn to primary infrastructureClean restoration, no errors
πŸ“Š Monitoring Alert TestMonthlyCloudWatch, Route 53 health checksAlerts delivered within 5 minutes
πŸ“‹ Recovery Runbook TestQuarterlyExecute documented proceduresAll steps executable, documentation accurate
πŸ” Security Incident DrillAnnualAWS account compromise scenarioCredentials revoked, service restored on DR

🎯 Testing Methodology

Quarterly Origin Failover Test:

  1. πŸ”§ Temporarily deny CloudFront access to S3 us-east-1 via bucket policy (add temporary Deny statement for CloudFront Origin Access Identity)
  2. ⏱️ Measure CloudFront automatic failover time to eu-west-1
  3. βœ… Verify content served from eu-west-1 origin
  4. πŸ”™ Remove the temporary Deny from us-east-1 bucket policy and confirm failback to primary origin
  5. πŸ“ Document results and improvements

Semi-Annual DNS Failover Test:

  1. πŸ”§ Update Route 53 health check to force failure
  2. ⏱️ Measure DNS propagation time
  3. βœ… Verify GitHub Pages serving traffic
  4. πŸ”™ Restore Route 53 health check
  5. πŸ“ Document results and TTL impact

πŸ“Š Business Continuity Metrics

🎯 Performance Tracking

MetricTargetCurrent StatusTrend
🎯 Availability99.998%99.999% (YTD)βœ… Exceeding
⚑ Origin Failover RTO< 30 seconds18 seconds (last test)βœ… On track
🌐 DNS Failover RTO15 minutes14 minutes (last test)βœ… On track
πŸ’Ύ Data Synchronization0 RPO0 seconds (real-time)βœ… On track
πŸ§ͺ BCP TestingQuarterlyLast tested 2026-02βœ… Current
πŸ“Š Monitoring Coverage100%100% (all endpoints)βœ… Complete

Note: The "Current Status" values in this table are illustrative planning examples. Actual operational metrics are monitored via AWS CloudWatch, Route 53 health check logs, and GitHub Pages status, and documented in operational runbooks.


🏒 Single-Person Company Adaptation

Hack23 AB Single-Person BCP Model

As CEO/Founder is the sole employee, traditional business continuity teams are not possible. Riksdagsmonitor implements automated infrastructure resilience + comprehensive documentation:

🎯 CEO As Business Continuity Coordinator

Capabilities:

  • Cloud Infrastructure Expertise: AWS Solutions Architect, 15+ years experience
  • Automated Failover: CloudFront origin failover, Route 53 health checks (no manual intervention)
  • Documentation: All procedures documented in ISMS for continuity
  • Monitoring: CloudWatch alarms, Route 53 health checks, automated notifications
  • Supplier Relationships: AWS Enterprise Support, GitHub Enterprise Support

🎯 Compensating Controls

Control TypeImplementationEffectiveness
πŸ€– Automated FailoverCloudFront origin failover (< 30s), Route 53 DNS failover (15 min)Eliminates manual recovery for primary scenarios
πŸ“š DocumentationComplete runbooks in BCPPlan.md, ARCHITECTURE.md, SECURITY_ARCHITECTURE.mdEnables recovery by any technical professional
πŸ”„ Infrastructure-as-Code (Planned)AWS static site and DNS infrastructure to be codified in Terraform/CloudFormation (see FUTURE_SECURITY_ARCHITECTURE.md)Future-state: fully reproducible infrastructure from version-controlled IaC
πŸ“Š Comprehensive MonitoringCloudWatch, Route 53 health checks, automated alertsReal-time detection and notification
πŸ’Ύ Geographic RedundancyMulti-region S3 (us-east-1 + eu-west-1), GitHub Pages standbyNo single point of failure

πŸ—οΈ Architecture & Security

πŸ”§ Operations

ℹ️ Alignment notice: WORKFLOWS.md, FUTURE_SECURITY_ARCHITECTURE.md and THREAT_MODEL.md are pending update to fully align with the dual-deployment continuity model and current primary hosting described in this BCPPlan. If there is any conflict regarding the current hosting/deployment architecture, this BCPPlan is the authoritative source.



πŸ“– Incident Response Playbooks

This section provides detailed, step-by-step incident response playbooks for the three highest-probability incident scenarios for Riksdagsmonitor. All playbooks follow the PICERL framework: Preparation, Identification, Containment, Eradication, Recovery, Lessons Learned.


Playbook 1: Content Tampering Incident

Playbook ID: IR-PB-001
Version: 1.0
Owner: James Pether SΓΆrling, CEO
Last Reviewed: 2026-02-25

Trigger Conditions and Detection Signals

This playbook activates when any of the following are detected:

SignalDetection MethodSeverity Indicator
Unexpected content changes in productionGitHub Actions diff in deploy logHIGH if unauthorized
Unauthorized Git commits to main branchGitHub audit log alertCRITICAL
Branch protection bypass detectedGitHub security eventCRITICAL
Anomalous content detected by user reportUser email to security@hack23.comHIGH
SLSA attestation failureGitHub Actions security jobHIGH
Unexpected language content injectionHTMLHint content validationMEDIUM

Severity Classification

SeverityCriteriaResponse TimeEscalation
P1 - CriticalUnauthorized content in production, branch protection bypass, SLSA attestation failure15 minutes to containmentImmediate personal notification to CEO
P2 - HighSuspected tampering unconfirmed, anomalous content flagged1 hour to investigationAlert within 30 minutes
P3 - MediumMinor unexpected changes, validation warnings4 hours to resolutionStandard ISMS notification

Step-by-Step Response Procedure

PHASE 1: DETECT (0-15 minutes for P1)

  1. Receive Alert β€” GitHub Actions notification, user report, or automated monitoring
  2. Verify Authenticity β€” Confirm alert is genuine (not false positive)
    • Check GitHub Actions run logs for the deploy job
    • Verify SHA-256 hashes in build metadata
    • Review Git commit history on main branch
  3. Classify Severity β€” Apply classification matrix above
  4. Document Start Time β€” Record incident start timestamp in UTC
  5. Open Incident Record β€” Create GitHub Issue with label security-incident

PHASE 2: TRIAGE (15-30 minutes for P1)

  1. Scope Assessment β€” Which files are affected? (index.html, all 14 language variants, news articles?)
  2. Impact Assessment β€” Is tampered content currently visible to users?
  3. Source Identification β€” Review GitHub audit log for:
    Settings > Security > Audit log
    Filter: Action = "repo.create_actions_secret" or "git.push" or "protected_branch"
    
  4. Blast Radius β€” Determine if compromise is isolated or widespread

PHASE 3: CONTAIN (30-60 minutes for P1)

  1. Immediate Rollback β€” Revert to last known good commit:
    git log --oneline -20  # Identify last known good commit
    git revert HEAD...<last-good-sha>  # Revert to good state
    git push origin main  # Trigger redeploy
    
  2. Block Malicious User (if external) β€” Via GitHub repository settings
  3. Revoke Compromised Credentials β€” If credentials were used:
    • Rotate all GitHub Secrets immediately
    • Revoke compromised PATs
    • Regenerate Amazon Bedrock API keys
  4. Enable Temporary Maintenance Mode β€” If content integrity cannot be confirmed:
    • Temporarily set CloudFront to return 503 for affected paths
    • Display maintenance page with explanation

PHASE 4: ERADICATE (1-4 hours)

  1. Root Cause Analysis β€” Determine exact attack vector:
    • Social engineering?
    • Compromised credentials?
    • Supply chain attack via dependency?
    • GitHub Actions workflow injection?
  2. Remove Malicious Content β€” Clean all affected files
  3. Verify Clean State β€” SHA-256 comparison against last known good
  4. Patch Vulnerability β€” Fix the root cause (update dependency, revoke credential, harden workflow)

PHASE 5: RECOVER (4-24 hours)

  1. Restore Service β€” Deploy verified clean content
  2. Verify Integrity β€” Automated integrity checks pass
  3. Monitor Closely β€” Increased monitoring for 72 hours post-incident
  4. Stakeholder Communication β€” Post transparent incident report (see template below)

PHASE 6: POST-INCIDENT (Within 72 hours)

  1. Lessons Learned Meeting β€” Document findings
  2. Update Controls β€” Implement additional preventive measures
  3. Update Threat Model β€” If new attack vector discovered
  4. NIS2 Assessment β€” Determine if ENISA notification required

Communication Template

Subject: [Riksdagsmonitor] Security Incident Report - Content Integrity

Incident: Potential content tampering detected
Date/Time: [UTC timestamp]
Severity: [P1/P2/P3]
Status: [Investigating / Contained / Resolved]

Summary:
We detected [brief description]. Our investigation found [findings].

Actions Taken:
1. [Action taken]
2. [Action taken]

Impact:
Content was [not affected / affected for X minutes] between [time] and [time].

Preventive Measures:
[Measures implemented to prevent recurrence]

Contact: security@hack23.com

Rollback Procedure Using Git History

# Step 1: Identify good commit
git log --oneline --graph --all | head -30

# Step 2: Verify content of last known good commit
git show <good-sha>:index.html | sha256sum

# Step 3: Create revert commit (preserves history)
git revert --no-commit <bad-sha>..<HEAD>
git commit -m "security: revert content tampering incident [IR-PB-001]"

# Step 4: Push and trigger redeploy
git push origin main

# Step 5: Verify production content
curl -s https://riksdagsmonitor.com/ | sha256sum

Evidence Collection Checklist

  • GitHub Actions run logs (download and archive)
  • GitHub Audit Log export for incident timeframe
  • Git commit history with diff
  • SHA-256 hashes of affected and clean files
  • CloudFront access logs for incident timeframe
  • SLSA attestation records
  • Sigstore transparency log entries
  • Browser screenshots of tampered content (if visible)
  • User reports with timestamps
  • Credential access logs from GitHub

Playbook 2: MCP Service Outage Incident

Playbook ID: IR-PB-002
Version: 1.0
Owner: James Pether SΓΆrling, CEO
Last Reviewed: 2026-02-25

Trigger Conditions

SignalDetection MethodSeverity
GitHub Actions MCP job failureWorkflow notification emailHIGH
riksdag.se API returning 5xx errorsPipeline error logHIGH
IMF upstream unavailable (data.imf.org / api.imf.org 5xx, DNS, or TLS error)scripts/imf-client.ts retry-exhausted logLOW (optional enrichment β€” graceful fallback to cached analysis/data/imf/ snapshots)
API timeout after 30sMCP client timeout logMEDIUM
Data staleness alert (>48h)Automated staleness checkerMEDIUM
Amazon Bedrock API unavailableGitHub Actions job failureHIGH
Zero articles generated for 3+ daysManual monitoring checkHIGH
CIA platform export unavailableDashboard shows stale dataMEDIUM

Severity Classification

SeverityCriteriaResponse Time
P1 - CriticalComplete MCP pipeline down, 0 data updates for 24+ hours1 hour
P2 - HighPartial data failure, degraded content generation, 12-24 hour gap4 hours
P3 - MediumSingle source unavailable, minor staleness, pipeline flaky24 hours

Step-by-Step Response Procedure

PHASE 1: DETECT AND VERIFY

  1. Confirm Outage β€” Check GitHub Actions run history:
    • Navigate to Actions tab
    • Filter by workflow: news-generation.yml
    • Check last 5 runs for failure pattern
  2. Identify Scope β€” Determine which component is failing:
    • Riksdag API unavailable?
    • Amazon Bedrock rate limited or unavailable?
    • riksdag-regering-mcp server issue?
    • Network egress blocked by harden-runner?
  3. Check External Status Pages:
  4. Classify Severity and start incident timer

PHASE 2: TRIAGE

  1. Check Cached Data Availability β€” Verify cia-data/ directory has recent data
  2. Determine User Impact β€” Are dashboards showing stale data? How stale?
  3. Estimate Recovery Time β€” Is this an external outage (wait) or internal issue (fix)?

PHASE 3: CONTAIN / GRACEFUL DEGRADATION

  1. Activate Stale Data Banner β€” If data is more than 48 hours old:
    • Edit index.html to show data freshness warning
    • Deploy immediately
  2. Use Cached Data β€” Pipeline automatically falls back to cia-data/ cache
  3. Disable Failed Pipeline β€” If pipeline is producing errors, temporarily disable cron:
    # Temporarily comment out schedule trigger in workflow YAML
    # on:
    #   schedule:
    #     - cron: '0 1 * * *'
    
  4. Document Outage Start β€” Record in incident log

PHASE 4: INVESTIGATE AND RESTORE

  1. External Outage: Wait for provider recovery, monitor status pages
  2. Internal Issue - API Change:
    • Review Riksdag API changelog
    • Update MCP server configuration
    • Test with npm run test:mcp
  3. Internal Issue - Credential:
    • Verify Amazon Bedrock API key in GitHub Secrets
    • Rotate key if expired or compromised
  4. Internal Issue - Rate Limiting:
    • Implement exponential backoff
    • Reduce fetch frequency temporarily
    • Check Riksdag API terms of service

PHASE 5: RESTORE SERVICE

  1. Re-enable Pipeline β€” Restore cron schedule in workflow
  2. Run Manual Trigger β€” workflow_dispatch to verify pipeline works
  3. Verify Output β€” Confirm articles generate successfully in all 14 languages
  4. Remove Stale Banner β€” Update HTML once fresh data available
  5. Verify Dashboards β€” Confirm CIA data dashboards show current data

PHASE 6: POST-INCIDENT

  1. Document Root Cause β€” In incident GitHub Issue
  2. Add Monitoring β€” Alert if no successful pipeline run in 36 hours
  3. Update Runbooks β€” If new failure mode discovered
  4. Resilience Improvement β€” Implement recommendation from this incident

Service Restoration Checklist

  • MCP server responding to tool discovery
  • Riksdag API returning valid JSON
  • Amazon Bedrock API responding within 30s
  • News generation pipeline completes without error
  • 14 language articles successfully generated
  • SHA-256 integrity check passes
  • Git commit and PR created successfully
  • CIA data dashboards showing fresh data
  • Stale data banners removed from all 14 language pages
  • GitHub Actions workflow next scheduled run confirmed

Communication Template

Subject: [Riksdagsmonitor] Service Notification - Data Pipeline Status

Status: [Investigating / Degraded / Restored]
Affected: Automated news generation and/or data dashboard updates
Date: [UTC date]

Current Status:
The automated data pipeline is [description]. 
Content published before [timestamp] remains accurate.

Expected Resolution:
[ETA or "Awaiting external provider recovery"]

Data Freshness:
Most recent data: [timestamp]
Best available data is displayed with staleness indicator.

Updates: Follow https://github.com/Hack23/riksdagsmonitor/issues

Playbook 3: Data Poisoning / Integrity Incident

Playbook ID: IR-PB-003
Version: 1.0
Owner: James Pether SΓΆrling, CEO
Last Reviewed: 2026-02-25

Trigger Conditions

SignalDetection MethodSeverity
Anomalous political content in generated articlesHuman review gateCRITICAL
SHA-256 hash mismatch for CIA data exportIntegrity check in pipelineHIGH
JSON schema validation failure from unexpected fieldsData validation logHIGH
Statistics that contradict known parliamentary dataQuality scoring below thresholdHIGH
Dramatic unexpected change in voting statisticsAnomaly detectionHIGH
LLM output contains factually incorrect political claimsHuman reviewMEDIUM
Unexpected HTML injection in article contentHTMLHint detectionMEDIUM

Severity Classification

SeverityCriteriaResponse Time
P1 - CriticalConfirmed false political information published and live15 minutes to takedown
P2 - HighSuspected data poisoning, anomalous content caught by review1 hour investigation
P3 - MediumData anomaly detected, not yet published4 hours analysis

Step-by-Step Response Procedure

PHASE 1: DETECT

  1. Initial Detection β€” Via human review gate, quality scoring, or user report
  2. Preserve Evidence β€” Before any changes:
    • Screenshot anomalous content
    • Download and archive current cia-data/ directory
    • Export GitHub Actions run log
    • Record all timestamps in UTC
  3. Initial Assessment β€” Is this:
    • LLM hallucination (most likely)?
    • Corrupted source data from Riksdag API?
    • Malicious injection into CIA platform export?
    • Supply chain compromise in MCP server?

PHASE 2: TRIAGE

  1. Trace to Source β€” Identify where anomalous data entered:
    # Check raw API response data
    cat cia-data/raw-export.json | jq '.["votingStats"]'
    
    # Compare with previous good data
    git diff HEAD~1 -- cia-data/
    
    # Check MCP tool call logs in GitHub Actions
    # Navigate: Actions > run-id > news-generation > step-logs
    
  2. Scope Assessment β€” How much content is affected?
  3. Published vs Pending β€” Is anomalous content live or only in pipeline?

PHASE 3: CONTAIN

  1. If Content Is Live β€” Immediate quarantine:
    # Revert to last clean version
    git revert HEAD --no-commit
    git commit -m "security: quarantine poisoned content [IR-PB-003]"
    git push origin main
    
  2. Pause Pipeline β€” Disable automated news generation until source validated:
    • Comment out cron schedule in workflow YAML
    • Push change to temporarily halt pipeline
  3. Quarantine Data Files β€” Move suspicious data to quarantine directory:
    mkdir -p cia-data/quarantine/$(date +%Y%m%d)
    cp cia-data/*.json cia-data/quarantine/$(date +%Y%m%d)/
    
  4. Update Cache β€” Restore from last verified clean data backup (Git history)

PHASE 4: VALIDATE SOURCE DATA

  1. Cross-Reference with Riksdag.se β€” Manually verify key statistics:
  2. Verify CIA Platform Data β€” Check CIA platform directly:
  3. Re-fetch Clean Data β€” Trigger fresh MCP data fetch after source verified:
    npm run fetch:cia-data  # Fetch fresh data
    npm run validate:data   # Run validation suite
    
  4. Schema Comparison β€” Verify data structure matches expected schema:
    npm run validate:schema -- --input cia-data/export.json
    

PHASE 5: ERADICATE

  1. Remove All Poisoned Content β€” From production and Git history if needed
  2. Re-validate All Published Articles β€” Check recent articles against source data
  3. Update Quality Filters β€” Add detection rules for the anomaly type seen
  4. Enhance LLM Guardrails β€” Add explicit factual verification prompts

PHASE 6: RECOVER

  1. Re-enable Pipeline β€” Restore cron schedule after validation
  2. Generate Fresh Articles β€” Replace any quarantined content
  3. Issue Correction β€” If incorrect information was public, issue transparent correction
  4. Enhanced Monitoring β€” Increase review frequency for 30 days

PHASE 7: POST-INCIDENT

  1. Root Cause Report β€” Document in incident GitHub Issue
  2. Control Enhancement β€” Implement additional preventive measures
  3. Threat Model Update β€” Update THREAT_MODEL.md with new attack vector
  4. Communication β€” If users were exposed to false information, issue public statement

Root Cause Analysis Template

## Data Poisoning Incident RCA - [DATE]

**Incident ID:** IR-PB-003-[YYYYMMDD]
**Severity:** [P1/P2/P3]
**Detection Time:** [UTC]
**Containment Time:** [UTC]
**Resolution Time:** [UTC]

### Timeline
| Time (UTC) | Event |
|------------|-------|
| [time] | Anomaly first detected by [method] |
| [time] | [Action taken] |

### Root Cause
[Describe the root cause: LLM hallucination / API corruption / supply chain]

### Attack Vector (if malicious)
[Describe how attacker introduced false data]

### Impact Assessment
- Content affected: [list of files/articles]
- Time live: [duration if published]
- User exposure: [estimated unique users who may have seen false content]

### Remediation Steps Taken
1. [Step taken]
2. [Step taken]

### Preventive Measures Implemented
1. [Control enhancement]
2. [Control enhancement]

### Lessons Learned
[Key takeaways for future incident prevention]

Preventive Measures

MeasureImplementationStatus
Human review gate for all AI-generated contentMandatory PR review before mergeActive
Quality score threshold (0.8/1.0)LLM self-evaluation before translationActive
SHA-256 integrity hashingEvery article and data fileActive
JSON schema validationMulti-stage data validation pipelineActive
Anomaly detection for statistical outliersNumeric range validationActive
Source data cross-referenceManual spot-check quarterlyPlanned
LLM output factual verificationCitation requirement in promptsPlanned 2027
Automated fact-checking against Riksdag.seSelenium scraper validationPlanned 2028

Playbook Summary Reference Card

PlaybookIDP1 ResponseP2 ResponsePrimary ActionEvidence
Content TamperingIR-PB-00115 min contain1 hr containgit revert + credential rotationGitHub audit + SHA-256
MCP OutageIR-PB-0021 hr restore4 hr restoreGraceful degrade + pipeline fixActions logs + status pages
Data PoisoningIR-PB-00315 min takedown1 hr quarantineQuarantine + source validationData diff + cross-ref

πŸ“‹ Document Control:
βœ… Approved by: James Pether SΓΆrling, CEO
πŸ“€ Distribution: Public
🏷️ Classification: Confidentiality: Public
πŸ“… Effective Date: 2026-05-28
⏰ Next Review: 2026-08-28
🎯 Framework Compliance: ISO 27001 NIST CSF 2.0 CIS Controls


🌐 IMF API Outage β€” Business Continuity Scenarios

Effective: 2026-04-24 Β· Authoritative hub: analysis/imf/README.md Β· analysis/imf/agentic-integration.md Β· analysis/imf/indicators-inventory.json Β· analysis/imf/data-dictionary.md Β· .github/aw/ECONOMIC_DATA_CONTRACT.md

IMF dependency criticality

AssetCriticalityRTORPOFallback
IMF Datamapper RESTSTANDARD24hN/ALast cached vintage in analysis/imf/
IMF SDMX 3.0 endpointSTANDARD24hN/ALast cached vintage; cross-source SCB for SE-specific
IMF cache (analysis/imf/ + analysis/daily/*/economic-data.json)HIGH4hN/ARe-fetch from IMF on next workflow run

IMF outage scenarios

ScenarioTriggerDetectionResponse
IMF-BCP-01 IMF API outage <24hHTTP 5xx persistentWorkflow log + retry budget exhaustedServe last cached vintage; annotate articles "cache fallback"; auto-recover on resolution
IMF-BCP-02 IMF API outage >24hHTTP 5xx persistent β‰₯24hNews-* workflow runs fail with stale-vintage errorOpen issue; escalate to editorial; switch to SCB for SE-specific GDP/CPI; pause look-ahead workflows
IMF-BCP-03 WEO vintage cycle skipApr WEO not publishedimf-fetch.ts --healthcheck reports stale vintageAnnotate articles with last vintage + advisory; do not block publishing
IMF-BCP-04 IMF Datamapper schema breaking changeCI integration test failstests/imf-client.test.ts red on schedulePin client to last-known-good schema; emergency hotfix per Change Management policy
IMF-BCP-05 IMF licence change (attribution rule modified)Notification on imf.orgManual monitoring quarterlyUpdate article footer template; backfill existing articles
IMF-BCP-06 IMF rate-limit tighteningHTTP 429 spikeRate-limit metric alarmReduce concurrency; expand cache TTL; communicate to article authors

IMF + multi-provider resilience

The IMF-primary, WB-residue, SCB-Sweden split is itself a BCP control: a single-provider outage degrades but does not break the platform. Look-ahead article workflows degrade gracefully because:

  1. Last cached IMF vintage remains valid for 6 months by contract
  2. SCB national-accounts cover the highest-priority Swedish-specific indicators
  3. WB residue continues to flow for governance/environment/social classes

Canonical rule. Every economic claim in a Riksdagsmonitor article cites an IMF dataflow first; World Bank citations are reserved for governance, environment and social residue (the classes IMF does not publish). SCB is the Swedish-specific ground truth layer. See ECONOMIC_DATA_CONTRACT.md v2.1 for the banned-phrase list and vintage discipline (>6 mo β†’ annotation).


πŸ”— Hack23 Ecosystem

🌐 Platforms πŸ“¦ Open-Source Projects πŸ›‘οΈ Governance & Standards
πŸ—³οΈ Riksdagsmonitor β€” Swedish Parliament intelligence
πŸ‡ͺπŸ‡Ί EU Parliament Monitor β€” European coverage
πŸ•΅οΈ Citizen Intelligence Agency β€” political-data engine
🌐 Hack23 AB β€” corporate site
πŸ“° Hack23 Blog β€” engineering & policy
πŸ’Ό Hack23 on LinkedIn
πŸ—³οΈ Hack23/riksdagsmonitor
πŸ•΅οΈ Hack23/cia
πŸ‡ͺπŸ‡Ί Hack23/euparliamentmonitor
πŸ”Œ Hack23/european-parliament-mcp
βœ… Hack23/cia-compliance-manager
πŸ₯‹ Hack23/black-trigram
🏠 Hack23/homepage
πŸ›‘οΈ Hack23 ISMS-PUBLIC β€” public ISMS
πŸ”’ Information Security Policy
πŸ€– AI Policy
πŸ§ͺ Secure Development Policy
🎯 Threat Modeling Policy
⚠️ Vulnerability Management
🏷️ Classification Framework

OpenSSF Best Practices OpenSSF Scorecard ISO 27001:2022 NIST CSF 2.0 CIS Controls v8.1 Apache 2.0

πŸ—³οΈ Empower citizens Β· πŸ” Strengthen democratic accountability Β· πŸ•΅οΈ Illuminate the political process

Β© 2008–2026 Hack23 AB (Org.nr 559534-7807) Β· Maintainer: James Pether SΓΆrling, CISSP CISM