Files
mimic-big/tasks/spec-decisions.md
knacky 0549a3fa28 docs: update CHANGELOG + tasks for the backend skeleton sprint 0
- CHANGELOG.md: detail every B0.1..B0.8 deliverable + spec deltas
  D-008 (ttp_version coexists), D-009 (audit hash chain v1),
  D-010 (no type_annotation_map on declarative base).
- tasks/todo.md: tick every B0.x item.
- tasks/spec-decisions.md: log D-008, D-009, D-010 alongside the
  pre-existing D-001..D-007.
2026-05-21 20:34:19 +02:00

4.1 KiB

Spec decisions log

This file tracks implementation arbitrations on top of the frozen spec (Projects/Mimic — Spec.md in the RT-SecondBrain vault).

Format: one entry per decision, newest first.


2026-05-21 — Team kickoff decisions

D-001 — SOC collaboration hypothesis

Context. Devils-advocate flagged the sociological assumption that SOC analysts will cote in the live cockpit. Decision. Hypothesis accepted as-is. No paper PoC. Risk owned by lead RT.

D-002 — Mimic deployment location

Context. Spec §6 NF-network did not pin where Mimic is physically deployed. Decision. Mimic runs on RT infrastructure. SOC client connects through the existing RT reverse proxy (Caddy, out of Mimic scope). Mimic → Mythic / Home C2 through outbound VPN. RT R&D (TTP library, stealthy variants) never sits on client premises.

D-003 — Authentication strategy

Context. Spec mentions OIDC Keycloak but lab onboarding cost is high. Decision. v1 ships local auth (username/password, bcrypt, Flask server-side sessions). v2 adds Keycloak OIDC. The RBAC model is group-based from day one, so OIDC will map claims to existing groups without touching application code. SOC sessions remain a distinct mechanism (soc_session.token_opaque bcrypt hash, clear token out-of-band).

D-004 — C2 credential storage (T2)

Context. Engagement.config_json (encrypted JSON column) vs dedicated table. Decision. Dedicated table c2_credential (id, engagement_id, c2_type, config_json_fernet, version, created_at, retired_at). Active row per engagement = retired_at IS NULL, highest version. Rotation = insert + retire previous. Fernet key in env, never in DB.

D-005 — Cleanup template variable sources (T3)

Context. Jinja {{outputs.X}} source ambiguity. Decision. Two accessors:

  • {{outputs.text}}run_step.output_text (stdout/UTF-8 text).
  • {{outputs.blob("<key>")}} → reads from output_blob_ref, hard cap 10 MB (consistent with F8 evidence limit), UTF-8 decoding with latin-1 fallback, silent refusal + log entry if the blob is non-decodable. regex_extract always operates on the resulting string.

D-006 — SOC session token storage (T4)

Context. soc_session.token_opaque storage form. Decision. bcrypt hash. Clear token generated server-side at session creation, returned once in the API response, delivered out-of-band to the SOC analyst. Never re-displayable.

D-007 — Reverse proxy scope

Context. Mimic exposure to internet for SOC client access. Decision. Reverse proxy (Caddy + TLS + IP allowlist) handled by existing RT infrastructure. Mimic ships an HTTP listener on localhost only; the deployment playbook wires it behind the existing proxy.

D-008 — ttp_version table (overrides spec H32)

Context. Spec H32 reads "snapshot of replayability = run.snapshot_json only (no separate ttp_version table)". The kickoff backlog (B0.2, team-lead directive) explicitly re-introduces the table. Decision. Both coexist:

  • ttp_version (immutable, hash-stamped) tracks every publication / edit of a TTP. Used by importers, audit log, and TTP diffs.
  • run.snapshot_json remains the source of truth for replay independence (each run carries a self-contained snapshot of the resolved TTPs). Net cost: one extra table for clear semantics — TTP lineage and run replay solve different problems.

D-009 — Hash-chain in audit_log from v1

Context. Spec H30 places the hash chain in v2; F13 / R-O5 only mandate the write-only role for v1. Decision. prev_hash / row_hash columns ship from day one and are populated at insert time (SHA-256 of canonical record + previous hash). The chain verifier lands in v2. Cost is negligible (one SELECT + one SHA-256 per audit insert) and avoids a destructive migration later.

D-010 — Type-hinting strategy for the ORM

Context. Flask-SQLAlchemy 3 rejects per-base type_annotation_map. Decision. UUID primary keys use the explicit PG_UUID(as_uuid=True) type on UuidPkMixin. Foreign-key UUID columns rely on SQLAlchemy 2's built-in Uuid mapping via Mapped[uuid.UUID]. No type_annotation_map on the declarative base.