# Threagile checks a YAML architecture model against risk rules in CI

> Threagile is a Go toolkit for agile threat modeling that keeps the architecture description as a YAML file next to the code, runs built-in and custom risk rules over it, and renders diagrams, spreadsheets and a PDF report.

**Threagile/threagile** — Agile Threat Modeling Toolkit

- Repository: https://github.com/Threagile/threagile
- Website: https://threagile.io
- Stars: 785 · Forks: 167
- Language: Go
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/threagile-threagile

## The architecture model is a YAML file, not a drawing

Threagile starts from an unusual premise for a threat modeling tool. Instead of asking a team to draw a data flow diagram, it asks them to describe the architecture as a YAML file kept inside the IDE, next to the code it describes. When the toolkit runs, every standard risk rule, and any custom rules that are present, is checked against that model.

The model schema is documented separately in the repository, under docs/model.md, and the tool exposes a wide set of outputs: a PDF report that includes diagrams, an Excel sheet of risks, JSON for risks and for technical assets, a stats JSON, an Excel sheet of tags, plus a data flow diagram and a data asset diagram. Those two diagrams are on by default, and the flags to turn them off are named -generate-data-flow-diagram and -generate-data-asset-diagram.

The bet behind the YAML approach is that a text file can live in a pull request. A diagram in a design tool cannot be reviewed as a diff, so it usually goes stale between threat modeling sessions. A YAML file can be reviewed, commented on and versioned, and it can be executed against in CI, which turns threat modeling from a periodic workshop into a continuous check.

The cost is authoring effort. Writing a YAML architecture description by hand is real work, and the README acknowledges that editing YAML in a text editor is tough. Everything else in the project exists to reduce that friction.

## Includes and macros exist because hand writing YAML is the hard part

Two mechanisms address the editing problem, and they are documented separately under docs/includes.md and docs/macros.md.

Includes are the reusable fragment mechanism. The idea is that a piece of the model, for instance a shared component or a standard data store, is written once and pulled into multiple models rather than copied. For an organisation with a catalogue of standard services, that is the difference between a model that is updated and one that quietly diverges from reality.

Macros are the interactive mechanism, and they are more unusual. The help output describes model macros as macros capable of reading a model yaml file, asking you questions in a wizard-style and then updating the model yaml file accordingly. So instead of writing YAML by hand, you answer a set of questions and the tool edits your model file. There are two flags that drive this: -list-model-macros to print the available macros, and -execute-model-macro to run one by its ID. The README's own example runs a macro named add-build-pipeline, which suggests the intention is that a CI pipeline addition is described once as a question and answer pair and then applied consistently to every model.

There is a third aid for people who prefer their editor to do the work: the -create-editing-support flag, described as creating some editing support stuff in the output directory, and in the README's phrasing, syntax validation, autocompletion and live templates in your favourite IDE. The Makefile shows what this consists of, since the support/ directory ships schema.json, live-templates.txt and openapi.yaml alongside the report template and the technologies list.

## Custom risk rules are YAML scripts, so no Go build is needed

Threagile distinguishes between two ways to extend its rule set, and the difference matters for how a team can adopt it.

The built-in path is plugins. The flag -custom-risk-rules-plugins takes a comma-separated list of plugins, described as .so shared object file names with custom risk rules to load. That is compiled native code, and it has consequences in the build. The Dockerfile carries an explicit comment on the CGO setting, explaining that it cannot be disabled because otherwise plugins do not run. So the plugin route means your custom rules are Go code compiled into a shared object, with all that implies for your build pipeline and for cross-platform distribution.

The script path is different. Custom risk rules can be written as YAML scripts without compiling Go code, and the repository documents a script language reference, a guide for writing custom risk rules and how to test your scripts. For a team whose rules change weekly, or whose rules are written by security people rather than by the platform team, a YAML script is a far lower barrier. The existence of a testing guide for scripts also means the rules can be covered by their own tests, which is not something the README promises for plugin rules.

Rules themselves can be managed from the command line. -list-risk-rules prints the available rules, -skip-risk-rules takes a comma-separated list of rule IDs to skip, and -ignore-orphaned-risk-tracking makes the tool log risk tracking entries that do not match a concrete risk rather than treating them as errors. That last flag is a practical necessity once risk tracking is separated from the findings, since a tracker file that outlives a refactor will otherwise fail the build.

## Running Threagile through Docker, from example model to check

The README states that the easiest way to execute Threagile on the command line is via its Docker container. The container image is published as threagile/threagile on Docker Hub, and the first command to run prints the available flags:

```shell
docker run --rm -it threagile/threagile --help
```

The next step is to obtain a model to edit. The container can generate one for you, either a full example model or a minimal stub, and the README shows both with the working directory mounted into the container:

```shell
docker run --rm -it -v "$(pwd)":/app/work threagile/threagile --create-example-model --output /app/work
```

```shell
docker run --rm -it -v "$(pwd)":/app/work threagile/threagile --create-stub-model --output /app/work
```

The example model is named threagile-example-model.yaml and the stub is threagile-stub-model.yaml, and the repository ships the sources for both under demo/example and demo/stub. The stub is the one to start from for your own service.

Then you point the tool at your model. The model path defaults to threagile.yaml and the output directory defaults to the current directory, so a run over a file called threagile.yaml in the working directory needs no flags at all:

```shell
docker run --rm -it -v "$(pwd)":/app/work threagile/threagile --verbose --model /app/work/threagile.yaml --output /app/work
```

When you want to know which enum values the model accepts, the container prints them with -list-types, which saves a round trip through the schema documentation.

## Server mode turns the tool into a REST service for a pipeline

Threagile can run as a server instead of a one-shot command. The -server flag starts a server on the given port rather than doing command line execution, and the README gives the Docker invocation, including a shared memory size, a published port and a named volume mounted at /data:

```shell
docker run --rm -it --shm-size=256m -p 8080:8080 --name threagile-server --mount 'type=volume,src=threagile-storage,dst=/data,readonly=false' threagile/threagile -server 8080
```

The repository backs this up with a server/ directory at the top level and an OpenAPI description in support/openapi.yaml, which is one of the assets the Makefile installs. The service is built on the Gin web framework, which the go.mod file lists as a direct dependency, along with google/uuid for identifiers.

Why run it as a service rather than as a command? Two reasons come out of the flag list. The -model-macro mechanism and the -create-editing-support output are authoring aids that fit an editor integration, while the analysis itself is a batch job that wants to be triggered from a pipeline. And the risk tracking model, with its orphaned tracking handling, implies state that outlives a single execution, which a one-shot container invocation has to carry in files.

The server mode is newer and less described than the command line mode. The README does not document authentication, concurrency limits or how the REST endpoints map to the flags, so treat the OpenAPI description as the reference and confirm the deployment posture with the maintainers before exposing it beyond your CI network.

## Reports come out as PDF, Excel and JSON, with Graphviz behind the diagrams

The output surface is wide, and the flag names make the defaults clear. Generating the report PDF, the risks Excel sheet, the risks JSON, the stats JSON, the tags Excel sheet and the technical assets JSON are all enabled by default, as are the two diagrams. Each has a matching flag that turns it off, and the PDF report includes the diagrams.

The diagram rendering has a visible dependency. The Dockerfile adds graphviz and ttf-freefont to the final image, and the Makefile has a gv target that turns a diagram.gv file into a PNG with dot -Tpng. So the diagrams are Graphviz output rather than hand drawn graphics, which is consistent with the YAML model approach: the layout is derived from the model, and the text stays in the file.

The Excel and JSON outputs deserve attention because they are the inputs to other systems. A risks JSON file can feed a tracker, an Excel sheet can go to a risk owner, and the technical assets JSON gives security teams a machine readable inventory of what the model says the system contains. The -ignore-orphaned-risk-tracking flag ties into this workflow: once tracking is external, entries in the tracker that no longer correspond to a rule finding should be reported and ignored rather than failing the run.

There is a smaller but useful detail for compliance work: -print-license and -print-3rd-party-licenses print licence information, and the Makefile copies LICENSE.txt along with the report background PDF into the asset directory when installing. The third party dependency list in go.mod includes libraries such as excelize for the Excel output, go-chart for charts, gofpdf for PDF generation and yaml.v3 for the model parsing.

## MIT licensed, tagged v0.9.1 and stable, with the last push in April 2026

The project is MIT licensed, with the licence text in LICENSE.txt at the repository root and the same statement printed by the tool itself in its help output. For a tool that runs inside a build pipeline and produces internal risk reports, the licence is permissive enough that there is no obvious obstacle to adopting it, though the terms still apply to the code and the container image, and the -print-3rd-party-licenses flag exists because the bundled dependencies carry their own licences.

Release naming is a little unusual. The visible tags are v0.9.0 on 2024-02-03, v0.9.1 on 2024-07-30 and a tag called stable on 2024-11-20. The README does not explain the stable tag convention, so check what it points at before relying on it as your pinned version. The help output in the README also prints a different version string, Version: 1.0.0 with a build timestamp, which reflects the Docker image at the time the README was written rather than the GitHub tag list. Pinning by tag is therefore safer than trusting the printed version.

On activity, the repository is not archived and the last push was on 2026-04-08. That is more than six months before now, so treat the project as one that is not currently receiving regular updates rather than as an actively developed one. The README also states that efforts on the user interface are ongoing with a few attempts made, and that this is far from being ready, which is an honest signal about the direction of the project.

The upside of the arrangement is that the tool is self contained and readable. The Go module targets go 1.26.0, the Makefile exposes install, uninstall, test-all, lint, gosec and dist targets, and the Dockerfile runs the full test suite with go test ./... during the image build, so a team can build its own image from the repository and pin it rather than pulling a moving tag.

## Conclusion

Threagile fits teams that already keep their architecture in version control and want a machine check on it in the pipeline, particularly where custom risk rules matter more than the built-in catalogue. It does not fit a team that needs a visual modelling tool, since the README says UI work is far from ready, and it does not fit an assessment where a recognised methodology with fixed categories is required, since the rules are the tool's own set. Before adopting, run the tool once against a model of one of your real services and read the generated risks, because the tool's usefulness depends entirely on how faithfully your model matches reality, and note that the last push was on 2026-04-08 while the published tags are v0.9.1 and stable.

## FAQ

### How do I run Threagile for the first time?

The README says the easiest way is the Docker container: docker run --rm -it threagile/threagile --help prints the flags. You can then generate a starting model with --create-example-model or --create-stub-model, and execute the analysis with --model and --output pointing at your model file.

### What file does Threagile read and what does it write?

The input model defaults to threagile.yaml and the output directory defaults to the current directory. By default the tool writes a report PDF with diagrams, a risks Excel sheet, a risks JSON, a stats JSON, a tags Excel sheet, technical assets JSON, a data flow diagram and a data asset diagram.

### Can I write custom risk rules without writing Go code?

Yes. Custom risk rules can be written as YAML scripts without compiling Go code, and the repository documents a script language reference, a guide and a testing guide for them. A second option is loading compiled plugins with -custom-risk-rules-plugins, which takes .so shared object files, and the Dockerfile notes that CGO cannot be disabled because otherwise plugins do not run.

### Does Threagile have a graphical user interface?

Not a finished one. The README states that efforts on UI are ongoing and that there are few attempts to do it, although that is far from being ready. The interface that does exist is the wizard style model macros, listed with -list-model-macros and run with -execute-model-macro, plus editor support files created by --create-editing-support.

### Can Threagile be run as a server instead of a command?

Yes. The -server flag starts a server on the given port instead of doing command line execution, and the repository ships a server/ directory along with an OpenAPI description at support/openapi.yaml. The README example publishes port 8080 and mounts a named volume at /data.

## Sources

- [License: MIT](https://github.com/Threagile/threagile/blob/master/LICENSE)
- [Project website](https://threagile.io)
- [README](https://github.com/Threagile/threagile/blob/master/README.md)
- [Releases](https://github.com/Threagile/threagile/releases)
- [Threagile/threagile on GitHub](https://github.com/Threagile/threagile)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/threagile-threagile
