Incident Response Retainer
Full incident response lifecycle management -- retainer contracts, engagement activation, chronological timeline tracking, evidence chain-of-custody, stakeholder communications, and post-incident reviews (PIRs). Built for managed security services that operate on retainer-based IR engagements.
Overview
The IR Retainer module provides a complete incident response management platform for SOCaaS providers. It tracks retainer contracts (Platinum/Gold/Silver tiers), manages individual engagement lifecycles from activation through closure, maintains forensic evidence chain-of-custody, and supports structured stakeholder communications. Post-incident reviews capture lessons learned and generate action items for continuous improvement.
Why This Module Is Needed
- Managed security providers must track retainer hour consumption, SLA compliance, and contract timelines across multiple clients.
- Incident response engagements follow a strict lifecycle (activated → triaging → containing → eradicating → recovering → closed) that requires structured tracking.
- Regulatory and legal requirements demand forensic evidence chain-of-custody documentation.
- Stakeholder communication during an active incident must be timely, consistent, and auditable.
- Post-incident reviews (PIRs) are critical for root cause analysis and preventing recurrence.
What Was Proposed
- Retainer contract management with tiered SLAs (Platinum: 30-min response / 500 hours, Gold: 60-min / 250 hours, Silver: 120-min / 100 hours).
- Full engagement lifecycle with 6 phases: activated, triaging, containing, eradicating, recovering, closed.
- Chronological timeline with typed entries (action, finding, decision, note) and phase tagging.
- Digital evidence catalog with SHA-256 hashes and chain-of-custody tracking.
- Stakeholder communication system with templates for initial notification, status updates, executive briefs, containment achieved, engagement closure, and war room standups.
- Post-incident review (PIR) with root cause analysis, impact assessment, lessons learned, action items, and recommendations.
- Dashboard with retainer usage metrics, engagement status breakdown, and mean time to contain/close.
What's Built
- 3 demo retainer contracts: Platinum (500h, 30-min SLA), Gold (250h, 60-min SLA), Silver (100h, 120-min SLA)
- 5 realistic demo engagements: ransomware outbreak, BEC/CEO impersonation, supply chain compromise, insider threat data exfiltration, cloud credential leak
- Full 6-phase engagement lifecycle with automatic timestamp tracking (activated_at, first_response_at, contained_at, closed_at)
- Chronological timeline with 14 demo entries across all engagements, typed as action/finding/decision
- Evidence chain-of-custody with 8 demo items (disk images, memory dumps, malware samples, log files) with SHA-256 hashes
- Stakeholder communications with 5 demo records (email notifications, status updates, Slack war room messages)
- 6 communication templates: Initial Notification, Status Update, Executive Brief, Containment Achieved, Engagement Closure, War Room Standup
- Post-incident review (PIR) creation with root cause, impact assessment, lessons learned, action items, recommendations
- Dashboard summary with retainer usage stats, active/closed counts, mean time to contain/close, and severity/status breakdowns
- Full CRUD for engagements with status transition API that auto-creates timeline entries
- DB-first architecture with seamless demo-data fallback
- Comprehensive frontend dashboard with retainer cards, engagement list, timeline viewer, communication templates, and engagement detail modals
Architecture
Engagement Lifecycle
Data Flow
- Frontend pages call
/api/v1/ir-retainer/*endpoints via the sharedapiclient. - The router validates inputs using Pydantic schemas (e.g.
EngagementStatusUpdaterestricts valid phase values via regex). - The service attempts DB operations first. On failure, falls back to in-memory demo data with 3 retainers, 5 engagements, and associated timeline/evidence/comms.
- Status transitions (
PUT /engagements/{id}/status) automatically setcontained_atwhen entering "containing" andclosed_atwhen entering "closed", and insert a timeline entry for the phase change.
Source Files
| Layer | Path |
|---|---|
| Router | platform/api/app/routers/ir_retainer.py |
| Service | platform/api/app/services/ir_retainer.py |
| Models (6) | platform/api/app/models/ir_retainer.py |
| Schemas | platform/api/app/schemas/ir_retainer.py |
| Frontend Dashboard | platform/frontend/src/app/ir-retainer/page.tsx |
| Frontend Engagements | platform/frontend/src/app/ir-retainer/engagements/page.tsx |
| Frontend Engagement Detail | platform/frontend/src/app/ir-retainer/engagements/[id]/page.tsx |
| Frontend PIR | platform/frontend/src/app/ir-retainer/pir/page.tsx |
| Frontend Communications | platform/frontend/src/app/ir-retainer/communications/page.tsx |
Routing
Frontend Routes
| Route | Description |
|---|---|
/ir-retainer | Dashboard -- retainer cards with hour gauges, active engagements, recent timeline, communication templates |
/ir-retainer/engagements | Engagement list with severity/status filters and create engagement action |
/ir-retainer/engagements/[id] | Engagement detail -- timeline, evidence, communications, phase transition controls |
/ir-retainer/pir | Post-Incident Review creation and viewing |
/ir-retainer/communications | Communication templates and engagement communication history |
API Endpoints
All endpoints are under /api/v1/ir-retainer with the ir-retainer tag.
# Retainers
GET /api/v1/ir-retainer/retainers # List all retainer contracts
GET /api/v1/ir-retainer/retainers/{retainer_id} # Get single retainer
# Engagements
GET /api/v1/ir-retainer/engagements # List all engagements
POST /api/v1/ir-retainer/engagements # Create new engagement (retainer_id, title, severity, lead_analyst, team)
GET /api/v1/ir-retainer/engagements/{engagement_id} # Get detail (timeline, evidence, comms, PIR)
PUT /api/v1/ir-retainer/engagements/{engagement_id}/status # Update phase (auto-timestamps + timeline entry)
# Timeline
POST /api/v1/ir-retainer/engagements/{engagement_id}/timeline # Add timeline entry (phase, author, type, title, description)
# Evidence
POST /api/v1/ir-retainer/engagements/{engagement_id}/evidence # Add evidence (type, name, hash_sha256, collected_by)
# Communications
POST /api/v1/ir-retainer/engagements/{engagement_id}/communications # Send communication (type, subject, body, recipients)
# Post-Incident Review
POST /api/v1/ir-retainer/engagements/{engagement_id}/pir # Create PIR (summary, root_cause, impact, lessons, actions, recommendations)
# Dashboard & Templates
GET /api/v1/ir-retainer/summary # Dashboard summary (retainer stats, MTTC, MTTR, severity breakdown)
GET /api/v1/ir-retainer/communication-templates # 6 pre-built communication templatesPrerequisites
- FastAPI backend with async SQLAlchemy and PostgreSQL (or demo mode).
- Next.js 14+ frontend with the shared
apiclient. - Tenant middleware -- all endpoints read
request.state.tenant_id. - Database migrations for 6 tables:
ir_retainers,ir_engagements,ir_timeline,ir_evidence,ir_communications,ir_post_incident_reviews.
Data Model
IRRetainer
| Column | Type | Notes |
|---|---|---|
id | String(36) PK | UUID |
tenant_id | String(36) | Indexed |
tier | String(20) | platinum / gold / silver |
hours_allocated | Integer | Total contract hours |
hours_used | Float | Hours consumed across engagements |
response_sla_minutes | Integer | 30 / 60 / 120 by tier |
contract_start | DateTime | Contract start date |
contract_end | DateTime | Contract expiry date |
status | String(20) | active / expired / suspended |
IREngagement
| Column | Type | Notes |
|---|---|---|
id | String(36) PK | UUID |
retainer_id | String(36) FK | References ir_retainers.id |
tenant_id | String(36) | Indexed |
title | String(500) | Engagement title |
description | Text | Full description |
severity | String(20) | critical / high / medium / low |
status | String(30) | activated / triaging / containing / eradicating / recovering / closed |
lead_analyst | String(255) | Primary IR analyst |
team_members | JSON | Array of analyst names |
hours_spent | Float | Hours consumed on this engagement |
activated_at | DateTime | When engagement was activated |
first_response_at | DateTime | First analyst action timestamp |
contained_at | DateTime | Auto-set on "containing" transition |
closed_at | DateTime | Auto-set on "closed" transition |
IRTimeline
| Column | Type | Notes |
|---|---|---|
id | String(36) PK | UUID |
engagement_id | String(36) FK | References ir_engagements.id |
timestamp | DateTime | When the event occurred |
phase | String(30) | Phase at time of entry |
author | String(255) | Analyst name |
entry_type | String(20) | action / finding / decision / note |
title | String(500) | Entry headline |
description | Text | Full details |
metadata | JSON | Flexible metadata |
IREvidence
| Column | Type | Notes |
|---|---|---|
id | String(36) PK | UUID |
engagement_id | String(36) FK | References ir_engagements.id |
evidence_type | String(50) | disk_image / memory_dump / log_file / pcap / malware_sample / screenshot / other |
name | String(500) | Filename or artifact name |
hash_sha256 | String(64) | SHA-256 integrity hash |
collected_by | String(255) | Analyst who collected evidence |
collected_at | DateTime | Collection timestamp |
chain_of_custody | JSON | Custody transfer log |
IRCommunication
| Column | Type | Notes |
|---|---|---|
id | String(36) PK | UUID |
engagement_id | String(36) FK | References ir_engagements.id |
comm_type | String(30) | email / slack / phone / status_update |
subject | String(500) | Message subject |
body | Text | Message body |
recipients | JSON | Array of email addresses or channels |
sent_by | String(255) | Analyst who sent |
sent_at | DateTime | Send timestamp |
PostIncidentReview
| Column | Type | Notes |
|---|---|---|
id | String(36) PK | UUID |
engagement_id | String(36) FK | References ir_engagements.id |
tenant_id | String(36) | Indexed |
summary | Text | PIR executive summary |
root_cause | Text | Root cause analysis |
impact_assessment | Text | Business impact |
lessons_learned | JSON | Array of lesson strings |
action_items | JSON | Array of action item objects |
recommendations | JSON | Array of recommendation strings |
created_by | String(255) | Author |
Relationships
IRRetainer→ has manyIREngagementIREngagement→ has manyIRTimelineIREngagement→ has manyIREvidenceIREngagement→ has manyIRCommunicationIREngagement→ has manyPostIncidentReview
UI Description
IR Command Center
Summary stat cards (Total Retainers, Active Engagements, Hours Used/Remaining, Avg Response Time, Open Action Items). Retainer cards with tier badges (platinum/gold/silver), hour gauges, SLA info, and contract dates. Active engagement list with severity badges, phase indicators, and analyst assignments. Recent timeline feed. Communication template cards with copy-to-clipboard and send actions.
Engagement Management
List view of all engagements with severity (critical/high/medium/low) badges, status phase indicators, lead analyst, hours spent, and SLA deadline. Create new engagement modal with retainer selection, title, severity, and team assignment. Click-through to engagement detail.
Engagement Detail View
Full engagement header with phase badge and team roster. Chronological timeline with colour-coded entry types (action=blue, finding=amber, decision=purple, note=grey). Evidence table with file names, types, SHA-256 hashes, and collection metadata. Communication log with sent messages. Phase transition controls.
Post-Incident Review
Create and view post-incident reviews for closed engagements. Structured form fields for summary, root cause analysis, impact assessment, lessons learned (bulleted list), action items (with owner and deadline), and recommendations.
Communication Center
6 pre-built communication templates (Initial Notification, Status Update, Executive Brief, Containment Achieved, Engagement Closure, War Room Standup) with variable placeholders. Communication history for each engagement with type badges (email/slack/phone/status_update), recipients, and timestamps.