- 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.
86 lines
4.1 KiB
Markdown
86 lines
4.1 KiB
Markdown
# 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.
|