Two changes scoped together since both stem from the post-PR2 wrap-up.
docs/podman-runner-setup.md (new, ~190 LOC):
Operational runbook for the gitea-runner host that drives CI. The first
attempt at install hit four traps that this archived version documents
so we don't lose the lesson:
1. `act_runner register` performs a sanity ping against the container
daemon before writing the credential. Without the Podman socket
mounted on the *register one-shot*, register fails silently and no
.runner file is produced. The runbook mounts the socket on both
register and daemon containers.
2. SELinux blocks rootless socket access by default. Quadlet
SecurityLabelDisable=true (or --security-opt label=disable for the
legacy CLI form) is the documented bypass. No-op on Debian, required
on RHEL/Fedora hosts.
3. The runner user UID is not 1000 on every host (gitea = 1005 here).
Quadlet `%U` substitution makes the unit portable; hardcoded UIDs
are explicitly called out as a sprint 0 mistake.
4. `podman generate systemd` is officially deprecated. Quadlet is the
only supported pattern going forward and is what this runbook ships;
legacy alternative is omitted on purpose.
Also captures: token placeholder convention (<TOKEN_FROM_GITEA_UI>,
never the real value in archived docs), single-use semantics, the
"secrets via file, not chat" convention, the `:X.Y.Z` pin policy versus
`:latest` in prod (ties into follow-up F-D1), and a decommissioning
section that cleans up state without nuking the user-level Podman socket.
tasks/todo.md:
New section "Frontend follow-ups (sprint 1+)" with F-D1..F-D5 from
code-reviewer on `chore/frontend-dockerfile` (649194b). All deferred,
none blocking. F-D1 (digest pinning) is project-wide and explicitly
references the backend image and the runner image alongside the
frontend ones for a single chore commit.
10 KiB
10 KiB
Sprint 0 — Mimic
Repo skeleton + foundational modules. Nothing that depends on PR1/PR2/PR3.
Backend (backend) — done in feature/backend-skeleton
- B0.1 —
backend/Python 3.12+ project:pyproject.toml(ruff, mypy strict, pytest, coverage 70 %),Makefile(Docker/Podman auto), multi-stageDockerfile,compose.ymlfor Postgres dev DB,.env.example. - B0.2 — Alembic baseline migration
202605210001_initial_schemacreates every table, enum, index, and the idempotent grants for the audit write-only Postgres role. Nottp_versiontable (D-009). Groupsrt_operator,rt_lead,soc_analystseeded with the exact F11 permission matrix (D-008). - B0.3 — SQLAlchemy 2 typed mapped classes for every spec §8 aggregate (engagement, host, user/group RBAC, ttp, scenario/scenario_step, run/run_step/cleanup, detection, evidence, report, soc_session, c2_credential, audit_log).
- B0.4 —
C2ConnectorABC + dataclasses +payload_typeenum + factory keyed onc2_type. Mythic payload map populated; Home stays empty until PR2. - B0.5 — Jinja2 SandboxedEnvironment,
regex_extractfilter (google-re2hard dependency per D-011, raisesRuntimeErrorat boot if absent — norefallback), fail-loud no-match,{{ outputs.text }}/{{ outputs.blob() }}accessors reading gzip-compressed blobs with 10 MB cap. - B0.6 — bcrypt password helpers + SOC opaque token (256-bit url-safe, bcrypt-hashed) +
group-based RBAC matrix matching F11 +
@require_permdecorator. - B0.7 — Flat CRUD blueprints for engagements / hosts / TTPs / scenarios (incl. step
composition with F3 invariant
host.c2_type == scenario.c2_type). - B0.8 — pytest baseline: unit tests passing, integration scaffold ready
(testcontainers Postgres +
/healthzsmoke).
Backend follow-ups (sprint 1+)
Tracked from code-review verdict on feature/backend-skeleton @ 12d131c:
MINOR (8) — to schedule
- M1 — Replace
parse_uuidinteger-ish lookup withwerkzeugUUID converter on the routes (<uuid:eid>) to avoid the 404 on malformed strings being hidden by the 400 path. - M2 — Add OpenAPI generation (Pydantic 2 +
flask-pydantic-openapior hand-rolled). - M3 — Wire
flask-limiterfor/auth/local/login(NF-network). - M4 — Replace string-based
Engagement.statussetter with a typed transition method. - M5 — Introduce per-engagement read view that pre-joins
engagement_memberfor RT operator dashboards (current per-request join is fine for v1 traffic, but re-evaluate at scale). - M6 —
mimic-cli user createdoes not handle the SOC user-type (intended, but document and gate explicitly with a clean error message). - M7 — Add a
mimic-cliengagement add-member <uid> --role rt_operatorshortcut so the F11 scoping in MA6 is reachable from the CLI without manual SQL. - M8 — (fixed in MA1 follow-up commit) Initial migration docstring no longer
references
ttp_version.
NIT (6) — opportunistic
- N1 — Sort imports inside
mimic.db.models.__init__alphabetically for diff stability. - N2 — Extract the
_engagement_or_404duplicated body into a shared helper. - N3 — Replace the inline
Permission.TTP_PROMOTE not in permscheck inttps.pywith a second@require_perm-style decorator. - N4 — (fixed)
gunicornadded topyproject.tomldependencies. - N5 — Replace bare
getattr(current_user, "groups", frozenset())accesses by a thincurrent_groups()helper. - N6 —
tests/integration/conftest.pyusesdb.create_all()instead of running Alembic. Marked with a TODO; switch over once the F11 seed must be exercised in integration. Plan: convert toalembic upgrade headonce the audit role bootstrap lives in the playbook (D-010).
Frontend (ux-frontend)
- F0.1 —
frontend/Vite + React + TypeScript strict + Tailwind 4 + TanStack Query 5, eslint strict + prettier, Playwright skeleton. - F0.2 — Design system provisional: semantic tokens in
theme.css(status colors, RT accent, data mono / UI sans), dark-first palette, placeholder logo. - F0.3 — Wireframes (via
frontend-designskill) on mock data: Login + engagement selection, Live cockpit, Scenario composer, Report + MITRE matrix, TTP library + import. - F0.4 — Routing skeleton + role-aware layout shell (no real auth wired yet).
- F0.5 — Push
feature/frontend-skeleton, open PR for code-reviewer. - F0.6 — Polish M1-M3 from code-review (single SessionProvider, typo, fallback removal).
Frontend follow-ups (sprint 1+, non-blocking, from review NITs)
- N1 — Tighten
readMockSessionpayload validation when real auth wires up (currently checks onlyrole; should validate the fullSessionUsershape). - N2 — Replace the UI-side recomposition of the
MIMIC-RUN:marker in the cockpit's "Resolved command" panel withresolvedCommandTextreturned by the backend (run_step_cleanup.resolved_command_textfor cleanup, equivalent field for steps when exposed). - N3 — Wire
StatusRail.linkStateto the real WebSocket connection state once Flask-SocketIO is reachable (currently hardcoded'up'). - N4 — Unify
router.tsxand any future router helpers undersrc/routing/(single naming, no split between root file and folder). Addressed in F0.6:src/routing/Root.tsxintroduced;router.tsxleft at top level as the app-level entry that other code imports — split kept minimal. - N5 — Actually import Recharts somewhere (likely the MITRE matrix or a latency chart) since it's declared in README + package.json but not yet used.
- N6 — When vendoring IBM Plex woff2 into
public/fonts/, addpublic/fonts/LICENSE.txt(OFL-1.1) for license compliance. - N7 — Add
frontend/.env.exampleexposingVITE_API_BASE_URLandVITE_WS_URLonce the backend publishes endpoints.
Spec / Docs (spec-analyst)
- S0.1 — Cross-check the data model in B0.2 against §8 of the spec; report deltas before merge.
- S0.2 — Cross-check the RBAC matrix in B0.6 against F11; report deltas before merge.
- S0.3 — Maintain
tasks/spec-decisions.mdas new arbitrations land. - S0.4 — Open
docs/architecture.mdonce backend layout is committed.
Review (code-reviewer)
- R0.1 — Review each PR per the published charter; block on security/OPSEC violations.
- R0.2 — Verify mypy strict and ruff clean before approving any backend PR.
- R0.3 — Verify TS strict, no
useEffect(fetch), exhaustive deps before approving any frontend PR.
CI follow-ups (sprint 1+) (devops)
Raised by code-reviewer during review of chore/podman-and-ci (M2-M3 + N1-N6).
None blocking, all deferred to sprint 1+.
- M2 —
backend/Makefile$(COMPOSE)detection: invert legacydocker-composev1 probe, prefer the Compose v2 plugin ($(CONTAINER) compose) first. - M3 —
.gitea/workflows/ci.ymlbackend job: chainapt-get update && apt-get installin oneRUN-style step and droprm -rf /var/lib/apt/lists/*(no-op in an ephemeral CI container). - N1 — Smoke workflow
cat /etc/os-release | head -3→ usehead -3 /etc/os-release(moot once smoke.yml is removed; track here in case smoke is reintroduced). - N2 —
.gitea/workflows/ci.ymlpull_request:trigger: restrict tobranches: [main]to avoid double-running on PR retargets. - N3 — Anticipate single-runner serialization: jobs will queue. Plan a second
runner (different host or
capacity: >1) before scaling sprint 2+ workload. - N4 — Add top-level
concurrency: { group: ${{ github.ref }}, cancel-in-progress: true }to cancel superseded PR runs. - N5 — CI uses
MIMIC_DATABASE_AUDIT_URL == MIMIC_DATABASE_URL(same role). Acceptable for unit tests; integration tests covering the audit write-only role must provision a separatemimic_audit_writerrole in the Postgres service before they can run. - N6 — Cache pip + npm via
actions/cache@v4(verify Gitea Actions fork support before adoption; fallback to manual cache volume on the runner if unsupported). - FERNET-KEY — Provision
FERNET_KEY_TESTGitea repo secret before sprint 1 wiresc2_credential.config_fernet(D-004).config.py:32accepts an empty default at boot butFernet(b"")raisesValueErrorat first use.
Frontend follow-ups (sprint 1+) (devops)
Raised by code-reviewer during review of chore/frontend-dockerfile
(649194b). None blocking, all deferred to sprint 1+. Some have a project-
wide reach (F-D1 also covers the backend image and the runner image).
- F-D1 — Pin container images by minor + digest, not by tag alone. Scope :
frontend/Dockerfile(node:22-alpine, nginxinc/nginx-unprivileged:alpine),backend/Dockerfile(python:3.12-slim-bookworm), and the runner image referenced indocs/podman-runner-setup.md(gitea/act_runner:X.Y.Z). Harmonise the policy in a single chore commit. - F-D2 — Decide image-level
HEALTHCHECKdirectives vs delegating health probing to Caddy upstream. Document the choice indocs/deploy.md. - F-D3 — Security response headers (
X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Content-Security-Policy). Arbitrate ownership betweenfrontend/nginx.confand the Caddy outer layer to avoid duplication / conflict. - F-D4 — Enable response compression. Either
gzip on+gzip_typesinfrontend/nginx.conf(runtime), orvite-plugin-compression(precompute .br / .gz at build time, served viagzip_static). Pick one. - F-D5 — OCI image labels (
org.opencontainers.image.source,image.title,image.licenses,image.revision) on every Dockerfile. Useful for registry metadata and supply-chain attestation tooling.
Conventions
- Branches:
feature/<scope>,fix/<scope>,docs/<scope>,chore/<scope>. Long-lived:main. - Commits: Conventional Commits (
feat:,fix:,chore:,docs:,test:,refactor:). - PRs: each branch → review (
code-reviewer) → team-lead merges. - No direct push to
main.