Configuration

Every AttackTree configuration key with its default and effect: profiles, enforcement, risk scenario and blocking levels, simulation, risk acceptance, model rendering and OTM, and verification.

AttackTree reads one configuration per repository. Every key is optional, and each has a documented default.

# Where the file lives

specify extension add attacktree writes .specify/extensions/attacktree/attacktree-config.yml with every key at its default. Edit and commit it. Put machine-specific values in attacktree-config.local.yml next to it, which Spec Kit gitignores. The annotated source of the file is config-template.yml.

# Precedence

load_config() merges four layers, each overriding the one above it:

#LayerSource
1Extension defaultsconfig.defaults in extension.yml
2Project config.specify/extensions/attacktree/attacktree-config.yml
3Local overrides.specify/extensions/attacktree/attacktree-config.local.yml
4EnvironmentSPECKIT_ATTACKTREE_*

Merging is recursive: a mapping in a later layer is merged key by key into the earlier one. A scalar or a list is replaced outright: setting profiles: [agentic] discards the default [default] instead of appending to it. The default profile’s scales still apply in that case; only its proposal libraries are lost.

# Environment variables

Exactly three are recognised:

VariableEffect
SPECKIT_ATTACKTREE_ENFORCEMENTReplaces enforcement.
SPECKIT_ATTACKTREE_PROFILESReplaces profiles; comma-separated, whitespace trimmed.
SPECKIT_ATTACKTREE_SCENARIOReplaces risk.scenario.

Other nested keys cannot be set from the environment; use the local override file.

# Keys

# profiles

profiles: [default, agentic]

See Profiles. Profiles supply the scales and the proposal libraries. Default [default]. Each name resolves to profiles/<name>.yaml. The default profile’s scales are always the base; the first active profile that declares scales overlays them, and every profile adds archetypes, vector and control proposals, and zones. A feature’s own attack-tree.yaml wins over this setting: the engine reads attacktree.profiles from the tree first.

ProfileSupplies
defaultscales, 8 actor archetypes, 20 attack vector proposals with CAPEC references, 16 control proposals with ASVS 4.0.3 or NIST CSF references
agentic5 attack-surface zones, 4 agentic archetypes, 15 vectors with OWASP LLM and Agentic 2026, ATLAS, and MAESTRO references, 16 controls of which 5 are probabilistic

# enforcement

enforcement: warn   # warn | strict

Controls the exit code of the check command and the advice the agent gives. Default warn.

Worst findingwarnstrict
critical22
high11
medium01
low or none00

Under strict the check and simulate commands also state that implementation must not proceed while a CRITICAL finding or a blocking goal is open.

# risk

risk:
  block_on: [critical]
  scenario: current
  max_paths: 200
KeyDefaultEffect
block_on[critical]Residual risk levels that make a goal “blocking”: simulate exits 1, converge does not converge, unless an unexpired decision targets the goal.
scenariocurrentWhich control statuses count as active in simulate, render, and check metrics: none (no control, the baseline), current (implemented, verified), verified, planned (planned, implemented, verified), all (everything not rejected). Converge always uses verified; check A6 always uses all.
max_paths200Cap on enumerated paths per node, kept per actor so every actor’s most likely paths survive. Larger trees are flagged truncated (check A16) and choke points are computed on the sample.

# simulation

simulation:
  iterations: 2000
  seed: 42
  likelihood_spread: 15
KeyDefaultEffect
iterations2000Monte Carlo iterations; 0 disables the section.
seed42Random seed; identical inputs give identical results.
likelihood_spread15Points added and subtracted around each leaf likelihood when no likelihood_range is set. Probabilistic controls are always jittered by ± 0.15 of their surviving fraction.

simulate --iterations N --seed N overrides both for one run.

# risk_acceptance

risk_acceptance:
  require_owner: true
  max_duration_days: 180

Governs check A10 over decisions[]. A decision must carry owner, rationale, and expires; anything missing is a high finding. require_owner: false drops owner from that set. max_duration_days caps how far ahead expires may sit; exceeding it is a medium finding. An expiry in the past, or one that is not YYYY-MM-DD, is high.

# model

model:
  baseline: .specify/memory/attack-tree.yaml
  otm: ""
  write_requirements_to_spec: true
  render_markdown: true
  diagram: mermaid
KeyDefaultEffect
baseline.specify/memory/attack-tree.yamlProject-level tree inherited by each new feature tree for shared actors, assets, controls, and decisions. Read at init only, silently skipped when absent.
otm"" (off)Path, relative to the feature directory or the repository, of an Open Threat Model file that seed reads, A13 links against, and A11 hashes. Empty disables OTM interop.
write_requirements_to_spectruePublishes the CR-### block into spec.md between the attacktree markers. render --no-spec overrides it for one run.
render_markdowntrueWrites attack-tree.md. render --no-markdown overrides it for one run.
diagrammermaidmermaid embeds one flowchart per goal in attack-tree.md; none omits them.

# verification

verification:
  test_command: ""
  scanners: []
  evidence_markers: ["CR-"]
  test_dirs: [tests, test, spec, __tests__]
KeyDefaultConsumed byEffect
test_dirs[tests, test, spec, __tests__]engineDirectories walked when collecting evidence.
evidence_markers["CR-"]engineSubstrings searched in those files to tie a test to a requirement (CR-001, CR_001, CR001 all match).
test_command""agentThe command converge runs, once, when it is invoked with --run-tests. Without the flag nothing is executed even if a command is configured.
scanners[]agentScanner or micro-simulation outputs converge may cite as evidence, recorded with method: scan or simulation. Never as a verdict on its own.

The engine never executes test_command or scanners; converge-scan echoes them under config.verification, and the converge command prompt decides what to run. Running anything is opt-in: without --run-tests, converge reads artifacts and runs nothing.