threatcl GraphQL API Documentation
March 28, 2026 · View on GitHub
Overview
The threatcl GraphQL API provides a powerful, flexible way to query and analyze your threat models. The API is served by the threatcl server command and exposes all threat model data through a type-safe GraphQL interface.
Getting Started
Starting the Server
# Basic usage
threatcl server -dir ./examples
# With custom port
threatcl server -dir ./examples -port 3000
# With file watching for auto-reload
threatcl server -dir ./examples -watch
Accessing the API
- GraphQL Playground:
http://localhost:8080/ - GraphQL Endpoint:
http://localhost:8080/graphql - Health Check:
http://localhost:8080/health
CLI Query Execution
In addition to the GraphQL server, you can execute queries directly from the CLI using the threatcl query command. This is useful for:
- Automation and CI/CD pipelines
- Quick data extraction
- Shell scripting
- Testing queries before using with server
Basic Usage
# Execute a simple query
threatcl query -dir ./examples -query '{ stats { totalThreats } }'
# Query from a file
threatcl query -dir ./examples -file query.graphql
# With variables
threatcl query -dir ./examples \
-query 'query($name: String!) { threatModel(name: $name) { author } }' \
-vars '{"name": "Tower of London"}'
# Compact output for scripting
threatcl query -dir ./examples \
-query '{ stats { totalThreats } }' \
-output compact | jq '.data.stats.totalThreats'
Output Formats
pretty(default): Formatted JSON with indentation for human readabilityjson: Same as prettycompact: Single-line JSON for scripting and piping to tools likejq
CLI vs Server
| Feature | server | query |
|---|---|---|
| Interactive Playground | ✅ | ❌ |
| Long-running process | ✅ | ❌ (one-shot) |
| CI/CD friendly | ❌ | ✅ |
| Scriptable output | ❌ | ✅ |
| Startup time | ~1s | <100ms |
| Network required | ✅ | ❌ |
| Use case | Development, exploration, API integration | Automation, scripting, CI/CD |
See threatcl query --help for full options.
Schema Overview
The GraphQL schema is organized around the following main types:
ThreatModel- Complete threat model with all associated dataThreat- Individual threats within a threat modelControl- Security controls (mitigations) for threatsInformationAsset- Data assets being protectedDataFlowDiagram- Data flow diagrams (DFDs) for visualizing system architectureStatistics- Aggregated statistics across all threat models
Query Reference
Root Queries
threatModels
Retrieve all threat models, optionally filtered by criteria.
Signature:
threatModels(filter: ThreatModelFilter): [ThreatModel!]!
Arguments:
filter(optional): Filter criteriaauthor: String - Filter by authorinternetFacing: Boolean - Filter by internet-facing attributenewInitiative: Boolean - Filter by new initiative attributeinitiativeSize: String - Filter by initiative size (e.g., "Small", "Medium", "Large")
Example:
query {
threatModels(filter: { internetFacing: true }) {
name
author
attributes {
internetFacing
initiativeSize
}
}
}
threatModel
Retrieve a single threat model by name.
Signature:
threatModel(name: String!): ThreatModel
Arguments:
name(required): The exact name of the threat model
Returns: ThreatModel or null if not found
Example:
query {
threatModel(name: "Tower of London") {
name
author
description
threats {
description
impacts
}
}
}
threats
Search for threats across all threat models, optionally filtered.
Signature:
threats(filter: ThreatFilter): [Threat!]!
Arguments:
filter(optional): Filter criterianame: String - Filter by threat name (case-insensitive substring match)impacts: [String!] - Filter by impact types (e.g., ["Confidentiality", "Integrity"])stride: [String!] - Filter by STRIDE categories (e.g., ["Spoofing", "Tampering"])hasImplementedControls: Boolean - Filter threats with/without implemented controls
Example:
query {
threats(filter: { stride: ["Spoofing", "Elevation Of Privilege"] }) {
name
description
stride
impacts
controls {
name
implemented
}
}
}
Example (filtering by name):
query {
threats(filter: { name: "theft" }) {
name
description
impacts
threatModel {
name
}
}
}
informationAssets
Retrieve all information assets, optionally filtered by classification.
Signature:
informationAssets(classification: String): [InformationAsset!]!
Arguments:
classification(optional): Filter by information classification (e.g., "Confidential", "Restricted", "Public")
Example:
query {
informationAssets(classification: "Confidential") {
name
description
informationClassification
threatModel {
name
}
}
}
stats
Get aggregated statistics across all threat models.
Signature:
stats: Statistics!
Example:
query {
stats {
totalThreatModels
totalThreats
totalInformationAssets
totalControls
implementedControls
averageRiskReduction
}
}
Type Reference
ThreatModel
Represents a complete threat model.
Fields:
type ThreatModel {
name: String!
author: String!
description: String
link: String
diagramLink: String
createdAt: Int
updatedAt: Int
attributes: Attributes
additionalAttributes: [AdditionalAttribute!]!
informationAssets: [InformationAsset!]!
threats: [Threat!]!
useCases: [UseCase!]!
exclusions: [Exclusion!]!
thirdPartyDependencies: [ThirdPartyDependency!]!
dataFlowDiagrams: [DataFlowDiagram!]!
sourceFile: String!
}
Attributes
Standard threat model attributes.
Fields:
type Attributes {
newInitiative: Boolean
internetFacing: Boolean
initiativeSize: String
}
Threat
Represents a security threat.
Fields:
type Threat {
name: String!
description: String!
impacts: [String!]!
stride: [String!]!
controls: [Control!]!
informationAssetRefs: [String!]!
threatModel: ThreatModel!
}
Bidirectional Reference:
threatModelfield provides reverse navigation to the parent threat model
Control
Security control or mitigation for a threat.
Fields:
type Control {
name: String!
description: String!
implemented: Boolean!
implementationNotes: String
riskReduction: Int
attributes: [ControlAttribute!]!
}
InformationAsset
Data or information being protected.
Fields:
type InformationAsset {
name: String!
description: String
informationClassification: String!
source: String
threatModel: ThreatModel!
}
Bidirectional Reference:
threatModelfield provides reverse navigation to the parent threat model
DataFlowDiagram
Data flow diagram representing system architecture.
Fields:
type DataFlowDiagram {
name: String!
processes: [Process!]!
dataStores: [DataStore!]!
externalElements: [ExternalElement!]!
flows: [Flow!]!
trustZones: [TrustZone!]!
}
Statistics
Aggregated statistics across all threat models.
Fields:
type Statistics {
totalThreatModels: Int!
totalThreats: Int!
totalInformationAssets: Int!
totalControls: Int!
implementedControls: Int!
averageRiskReduction: Float
}
Calculations:
averageRiskReductionis only calculated when controls have risk reduction values > 0- Returns
nullif no controls have risk reduction values
Advanced Query Examples
Complete Threat Model with All Nested Data
query {
threatModel(name: "My Application") {
name
author
description
createdAt
updatedAt
sourceFile
attributes {
newInitiative
internetFacing
initiativeSize
}
informationAssets {
name
informationClassification
description
}
threats {
description
impacts
stride
controls {
name
description
implemented
implementationNotes
riskReduction
}
}
useCases {
description
}
exclusions {
description
}
thirdPartyDependencies {
name
description
saas
openSource
uptimeDependency
}
dataFlowDiagrams {
name
processes {
name
trustZone
}
dataStores {
name
trustZone
informationAsset
}
flows {
name
from
to
}
trustZones {
name
}
}
}
}
Filtering by Multiple Criteria
query {
# Internet-facing large initiatives
largeFacingInternet: threatModels(filter: {
internetFacing: true
initiativeSize: "Large"
}) {
name
author
}
# Threats with specific STRIDE categories
authThreats: threats(filter: {
stride: ["Spoofing", "Elevation Of Privilege"]
}) {
description
threatModel {
name
}
}
# Confidential assets
confidentialAssets: informationAssets(classification: "Confidential") {
name
threatModel {
name
}
}
}
Threat Models with Unimplemented Controls
query {
threatModels {
name
threats {
description
controls {
name
implemented
riskReduction
}
}
}
}
Then filter client-side for implemented: false.
Statistics Dashboard
query DashboardStats {
stats {
totalThreatModels
totalThreats
totalInformationAssets
totalControls
implementedControls
averageRiskReduction
}
allModels: threatModels {
name
threats {
description
controls {
implemented
}
}
}
}
Integration Examples
cURL
# Query statistics
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ stats { totalThreatModels totalThreats } }"}'
# Query specific threat model
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ threatModel(name: \"Tower of London\") { name author } }"}'
JavaScript/Node.js
const fetch = require('node-fetch');
async function queryThreatModels() {
const query = `
query {
threatModels {
name
author
threats {
description
}
}
}
`;
const response = await fetch('http://localhost:8080/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
});
const data = await response.json();
console.log(JSON.stringify(data, null, 2));
}
queryThreatModels();
Python
import requests
def query_stats():
query = """
query {
stats {
totalThreatModels
totalThreats
implementedControls
}
}
"""
response = requests.post(
'http://localhost:8080/graphql',
json={'query': query}
)
print(response.json())
query_stats()
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type GraphQLRequest struct {
Query string `json:"query"`
}
func main() {
query := `{ threatModels { name author } }`
reqBody, _ := json.Marshal(GraphQLRequest{Query: query})
resp, err := http.Post(
"http://localhost:8080/graphql",
"application/json",
bytes.NewBuffer(reqBody),
)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
File Watching
When the server is started with the -watch flag, it monitors the threat model directory for changes:
Monitored Events:
- File created (
.hcl,.json) - File modified (
.hcl,.json) - File deleted
- File renamed
Behavior:
- Changes are detected in real-time
- Cache is automatically updated
- No server restart required
- All active GraphQL connections continue to work
- Subsequent queries reflect the latest data
Example Output:
File modified: examples/tm1.hcl - reloading...
Successfully reloaded examples/tm1.hcl (6 threat models loaded)
Best Practices
Query Optimization
-
Request Only What You Need: GraphQL allows you to specify exactly which fields to return. Avoid over-fetching by requesting only the data you need.
-
Use Filters: Filter data at the server level rather than fetching everything and filtering client-side.
-
Avoid Deep Nesting: While the API supports deep nesting, excessive nesting can impact performance.
Error Handling
GraphQL returns errors in a structured format:
{
"errors": [
{
"message": "Error message here",
"path": ["fieldName"]
}
],
"data": null
}
Always check for the errors field in responses.
CORS
The server is configured to allow requests from any origin (*). For production use, consider restricting CORS origins.
Limitations
- Read-Only: The current API only supports queries. Mutations (create, update, delete) are not yet implemented.
- No Subscriptions: Real-time updates via GraphQL subscriptions are not supported. Use file watching (
-watchflag) for auto-reload instead. - No Pagination: Large result sets are returned in full. Consider filtering to reduce response size.
- In-Memory Only: All data is stored in memory. Server restart clears the cache and reloads from files.
Troubleshooting
Server Won't Start
Error: Port already in use
Solution: Use a different port with -port flag
Error: Directory not found
Solution: Verify the -dir path exists and contains .hcl or .json files
Query Returns Empty Results
- Verify threat models are loaded: Check server startup output for "Loaded X threat model(s)"
- Check the health endpoint:
http://localhost:8080/health - Verify filter criteria: Remove filters to see if data exists
- Check threat model names: Names are case-sensitive
File Watching Not Working
- Ensure
-watchflag is specified - Verify file extensions are
.hclor.json - Check file is in the watched directory or subdirectories
- Review server console output for error messages
Support
For issues, feature requests, or questions:
- GitHub Issues: https://github.com/threatcl/threatcl/issues
- Documentation: https://threatcl.dev/