System Design: Engagement Planning & Artifact Management Platform


1. Problem Statement

Design an internal platform for a red team consulting firm to manage engagement lifecycle: from scoping through authorization, execution, artifact management, deconfliction, and final reporting. The platform serves a team of 15–30 consultants running 10–20 concurrent engagements.

Functional requirements:

  • Engagement CRUD: create engagement, associate client, define scope, set dates
  • ROE document generation and signature tracking
  • Deconfliction log: timestamped log of operator actions for each engagement
  • Artifact vault: encrypted storage for engagement artifacts (screenshots, credential proofs, network captures); per-engagement access control
  • Report drafting: collaborative finding management (create/assign/review findings)
  • ATT&CK mapping: each finding links to one or more ATT&CK technique IDs
  • Status dashboard: engagement status, upcoming deadlines, finding counts by severity

Non-functional requirements:

  • Multi-tenant: findings and artifacts from one client never visible to another
  • Encryption: artifacts encrypted at rest; keys managed per engagement
  • Audit log: all access to artifacts and report drafts logged immutably
  • Availability: 99.9% uptime during active engagement windows (not 24/7 but business-hours-critical)
  • Access control: role-based (operator, engagement lead, partner/reviewer, admin)

2. Constraints

In scope:

  • Engagement and finding data model
  • Access control model
  • Artifact storage design
  • Deconfliction log design
  • API surface for operator tooling integration

Out of scope:

  • C2 infrastructure (covered in Design 01)
  • Client-facing portal
  • Automated exploitation tooling

3. High-Level Architecture

┌──────────────────────────────────────────────────────────────────────────┐
│  Web/CLI Clients (consultants, engagement leads, partners)               │
└───────────────────────────────┬──────────────────────────────────────────┘
                                │ HTTPS + mTLS (internal PKI)
                                ▼
┌──────────────────────────────────────────────────────────────────────────┐
│  API Gateway / Auth Layer                                                │
│  - Authenticate: OIDC SSO (internal IdP)                                 │
│  - Authorize: role-based access control per engagement                   │
│  - Rate-limit: prevent bulk artifact download                            │
└───────────────────────────────┬──────────────────────────────────────────┘
                                │
        ┌───────────────────────┼───────────────────────┐
        │                       │                       │
        ▼                       ▼                       ▼
┌──────────────┐   ┌────────────────────┐   ┌───────────────────┐
│  Engagement  │   │  Finding & Report  │   │  Artifact Vault   │
│  Service     │   │  Service           │   │  Service          │
│  - CRUD      │   │  - Finding CRUD    │   │  - Encrypted blob │
│  - ROE gen   │   │  - ATT&CK mapping  │   │    store          │
│  - Scope mgmt│   │  - Collab drafting │   │  - Per-engagement │
│  - Timeline  │   │  - Severity/review │   │    KMS key        │
└──────┬───────┘   └────────┬───────────┘   └────────┬──────────┘
       │                    │                        │
       └────────────────────┼────────────────────────┘
                            │
                            ▼
       ┌─────────────────────────────────────┐
       │  Shared: Deconfliction Log Service  │
       │  - Append-only timestamped records  │
       │  - Per-engagement access            │
       │  - Immutable audit trail            │
       └─────────────────────────────────────┘
                            │
                            ▼
       ┌─────────────────────────────────────┐
       │  Persistence Layer                  │
       │  PostgreSQL: structured data        │
       │  S3: artifact blobs (server-side    │
       │       AES-256 + client-side key)    │
       └─────────────────────────────────────┘

4. Component Deep-Dives

4.1 Engagement Data Model

-- Core engagement table
CREATE TABLE engagements (
    id          UUID PRIMARY KEY,
    client_name TEXT NOT NULL,
    code_name   TEXT NOT NULL,      -- e.g., "Cedar Lattice"
    start_date  DATE NOT NULL,
    end_date    DATE NOT NULL,
    status      TEXT NOT NULL,      -- planning | active | completed | archived
    kms_key_id  TEXT NOT NULL,      -- per-engagement encryption key ARN
    created_by  UUID NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

-- Scope definition (IP ranges, domains, cloud accounts)
CREATE TABLE scope_entries (
    id              UUID PRIMARY KEY,
    engagement_id   UUID REFERENCES engagements(id),
    entry_type      TEXT NOT NULL,   -- in_scope | out_of_scope
    description     TEXT NOT NULL,
    value           TEXT NOT NULL,   -- IP range, domain, AWS account ID, etc.
);

-- ROE document tracking
CREATE TABLE roe_documents (
    id              UUID PRIMARY KEY,
    engagement_id   UUID REFERENCES engagements(id),
    version         INT NOT NULL,
    content_hash    TEXT NOT NULL,   -- SHA-256 of the signed document
    signed_by_name  TEXT NOT NULL,   -- client authority
    signed_at       TIMESTAMPTZ,
    status          TEXT NOT NULL,   -- draft | pending_signature | signed
);

4.2 Access Control Model

Roles (per engagement, not global):

RoleWhat they can do
operatorRead engagement scope and ROE; write to deconfliction log; upload artifacts to engagement vault; create findings (draft only)
engagement_leadAll operator permissions + promote findings to review; edit scope; add operators; generate report draft
partner_reviewerRead-only for report review; can leave comments; can approve final report
adminFull access; cross-engagement visibility; manage encryption keys

Implementation: RBAC with engagement-scoped roles. A user can be an operator on one engagement and an engagement_lead on another simultaneously. Access check: (user_id, engagement_id, role) tuple lookup before every operation.

Key principle: no cross-engagement data access for non-admin roles. An operator on Cedar Lattice cannot list engagements or access artifacts from another engagement.

4.3 Artifact Vault

Security requirement: finding artifacts (screenshots of credential captures, network traffic extracts) are client-sensitive. If the platform is compromised, artifacts must not be readable without the engagement key.

Encryption model:

  • Each engagement has a unique KMS Customer Managed Key (CMK), created at engagement start
  • Artifacts are encrypted client-side (before upload) with the CMK
  • S3 stores only encrypted blobs
  • The CMK is accessible only to users with the operator or higher role on that engagement
  • At engagement archive (30 days post-end), the CMK is disabled; artifacts become unreadable without an admin CMK re-enable

Upload flow:

Operator → API: upload artifact
API: verify operator role on engagement
API: request data key from KMS (CMK for engagement)
API: encrypt artifact blob client-side with data key
API: store encrypted blob in S3, metadata (name, type, uploader, timestamp) in Postgres
API: return artifact ID to operator
KMS: log key usage to CloudTrail

Download flow (same in reverse):

Operator → API: download artifact ID
API: verify operator role on engagement
API: fetch encrypted blob from S3
API: decrypt data key via KMS (CMK for engagement)
API: decrypt blob, return to operator
KMS: log key usage

4.4 Deconfliction Log Service

Requirement: an immutable record of operator actions, used during deconfliction calls and for the engagement report's activity timeline.

Design:

  • PostgreSQL table with append-only INSERT permissions (no UPDATE, no DELETE via application role)
  • A separate read-only role for generating the deconfliction report
  • Fields: {id, engagement_id, operator_id, timestamp_utc, action_type, target_host, description, session_id}
  • action_type: enum of {scan, exploit, credential_dump, lateral_move, persistence, collection, tool_deploy, cleanup, deconfliction_stop, deconfliction_resume}

API: POST /engagements/{id}/deconfliction-log (operator writes an entry) GET /engagements/{id}/deconfliction-log?from=T&to=T (lead/partner reads the timeline)

Immutability guarantee: the application service role has INSERT only on the deconfliction_log table. UPDATE and DELETE are granted only to the admin role, which requires a separate authentication factor and generates a high-priority audit event.

4.5 Finding and Report Service

Finding state machine:

draft → under_review → approved → published
         ↑           ↓
         └── rejected ┘

Each finding in draft contains: title, severity, attck_id, precondition, evidence references (artifact vault IDs), detection opportunity, remediation, residual risk.

Collaborative editing: findings are individually locked during edit (optimistic lock with a 5-minute TTL). Last-write-wins within the lock window.

Report generation: the report generator pulls all approved findings, sorts by severity, and renders the Mandiant-style report structure: executive summary (auto-drafted from finding count and severity distribution), engagement overview, finding details, detection-gap matrix, remediation roadmap.


5. Tradeoffs

Monolith vs. microservices:

  • The five services (engagement, finding, artifact, deconfliction, auth) could be separate microservices. For a 15–30 person firm with 10–20 concurrent engagements, the scale does not justify microservice operational overhead.
  • Decision: modular monolith with clear service boundaries in code; can extract to microservices if the firm scales past 100 concurrent engagements.

PostgreSQL vs. document store for findings:

  • Findings have a flexible schema (fields added as methodology evolves). A document store (MongoDB, DynamoDB) handles schema evolution better.
  • But the engagement and user relationships require relational integrity (a finding belongs to one engagement, enforced by FK).
  • Decision: PostgreSQL with JSONB for the flexible finding_metadata field; relational tables for the fixed schema entities.

Client-side encryption vs. server-side:

  • Server-side encryption (SSE-S3, SSE-KMS) encrypts at rest but decrypts transparently on read — if the platform is compromised, artifacts are readable.
  • Client-side encryption means the platform itself never holds plaintext; compromise of the platform server does not expose artifact content.
  • Decision: client-side encryption (more secure, acceptable UX for a professional tool).

6. Detection-Forward Perspective

This platform is an internal defensive control for the red team firm itself:

  • The artifact vault's CMK audit trail in CloudTrail is the firm's own detection mechanism: if a CMK is used outside of business hours or by an unexpected user, that is an internal security alert.
  • The deconfliction log serves as the firm's own audit trail for legal protection.
  • The immutable finding state machine prevents retroactive modification of reported findings (important for professional integrity and potential legal scrutiny).

From an external attacker's perspective, this platform is a high-value target: it contains client credentials and vulnerability proof artifacts. The CMK-based artifact encryption and per-engagement key rotation are the primary data-loss mitigations.