Metadata and Extensions - ericfitz/tmi GitHub Wiki
Metadata and Extensions
Extend TMI with custom metadata on threat models, diagrams, threats, and other objects.
Overview
TMI supports extensible metadata on all major entity types, allowing you to customize the platform for your workflows and requirements. Metadata is stored as key-value pairs in a dedicated metadata table, keyed by entity type and entity ID.
What is Metadata?
Metadata consists of custom key-value pairs that you can add to:
- Threat models
- Diagrams
- Threats
- Notes
- Documents
- Repositories
- Assets
- Cells (diagram elements)
- Surveys
- Survey responses
- Teams
- Projects
Adding Metadata
Via User Interface
The UI provides a metadata dialog for managing key-value pairs on supported entities. The dialog is available for threat models, diagrams, threats, notes, documents, repositories, assets, teams, and projects.
- Open the entity (threat model, diagram, threat, etc.)
- Click the metadata icon button (a kebab/more menu item on teams and projects, or a direct button on other entities)
- In the metadata dialog, click "Add Metadata" to add a new row
- Enter the key and value in the inline table fields
- Click "Save" to persist changes
The dialog supports read-only mode for users without edit permissions. Entries with empty keys or values are automatically filtered out on save.
Metadata Constraints
- Key: 1-256 characters. Allowed characters: alphanumeric, hyphens (
-), underscores (_), dots (.), and colons (:). Must match^[a-zA-Z0-9._\-:]{1,256}$. - Value: String value up to 1024 bytes (UTF-8 encoded). HTML content is sanitized (tags stripped, injection patterns rejected).
- Bulk limit: A maximum of 20 metadata entries per bulk operation.
Common Metadata Uses
Categorization
category: web-application
tier: production
environment: cloud
Ownership and Responsibility
owner: security-team
reviewer: john.doe
team: platform-engineering
Compliance and Governance
compliance: PCI-DSS,HIPAA,SOC2
data-classification: confidential
regulatory-requirement: GDPR
Workflow Integration
jira-project: SEC
status: in-review
review-date: 2025-06-01
approval-required: true
Custom Classification
attack-surface: external
data-sensitivity: high
business-criticality: critical
Metadata Best Practices
Consistent Naming
- Use kebab-case, snake_case, or dot-separated keys (all are valid)
- Keys may contain only alphanumeric characters, hyphens, underscores, dots, and colons
- Spaces are not allowed in keys
- Be consistent across objects
- Document your schema
Structured Values
For multiple values, use delimiters:
- Comma-separated:
PCI-DSS,HIPAA,SOC2 - Pipe-separated:
team-a|team-b|team-c
Note: Values are plain strings (max 1024 bytes). HTML tags are stripped by the server.
Documentation
Document your metadata schema:
- What keys are used
- What values are valid
- What they mean
- When to use them
API Integration
Metadata is managed through dedicated sub-resource endpoints, not embedded in entity PATCH operations. Each entity type that supports metadata exposes the same set of endpoints.
Endpoint Pattern
For a given entity, metadata endpoints follow this pattern (example for threat models):
| Method | Path | Description |
|---|---|---|
GET |
/threat_models/{id}/metadata |
List all metadata for an entity |
POST |
/threat_models/{id}/metadata |
Create a single metadata entry |
GET |
/threat_models/{id}/metadata/{key} |
Get a specific metadata entry by key |
PUT |
/threat_models/{id}/metadata/{key} |
Update a specific metadata entry by key |
DELETE |
/threat_models/{id}/metadata/{key} |
Delete a specific metadata entry by key |
POST |
/threat_models/{id}/metadata/bulk |
Bulk create metadata entries (max 20) |
PUT |
/threat_models/{id}/metadata/bulk |
Bulk replace all metadata for an entity |
PATCH |
/threat_models/{id}/metadata/bulk |
Bulk upsert (create or update) metadata entries |
The same pattern applies to all supported entity types:
/threat_models/{threat_model_id}/diagrams/{diagram_id}/metadata/threat_models/{threat_model_id}/threats/{threat_id}/metadata/threat_models/{threat_model_id}/notes/{note_id}/metadata/threat_models/{threat_model_id}/documents/{document_id}/metadata/threat_models/{threat_model_id}/repositories/{repository_id}/metadata/threat_models/{threat_model_id}/assets/{asset_id}/metadata/teams/{team_id}/metadata/projects/{project_id}/metadata/admin/surveys/{survey_id}/metadata/intake/survey_responses/{survey_response_id}/metadata
Reading Metadata
Metadata is included as an array in entity responses:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Payment System",
"metadata": [
{ "key": "compliance", "value": "PCI-DSS" },
{ "key": "owner", "value": "security-team" },
{ "key": "review-date", "value": "2025-06-01" }
]
}
Listing metadata via the sub-resource endpoint returns the same array format:
GET /threat_models/550e8400-e29b-41d4-a716-446655440000/metadata
[
{ "key": "compliance", "value": "PCI-DSS" },
{ "key": "owner", "value": "security-team" }
]
Creating Metadata
Create a single entry:
POST /threat_models/550e8400-e29b-41d4-a716-446655440000/metadata
Content-Type: application/json
{ "key": "status", "value": "reviewed" }
Returns 201 Created. Returns 409 Conflict if the key already exists.
Updating Metadata
Update an existing entry by key (the request body contains only the value):
PUT /threat_models/550e8400-e29b-41d4-a716-446655440000/metadata/status
Content-Type: application/json
{ "value": "approved" }
Bulk Operations
Bulk create (all keys must be new):
POST /threat_models/{id}/metadata/bulk
Content-Type: application/json
[
{ "key": "priority", "value": "high" },
{ "key": "reviewer", "value": "john.doe" }
]
Bulk upsert (creates new keys, updates existing):
PATCH /threat_models/{id}/metadata/bulk
Content-Type: application/json
[
{ "key": "priority", "value": "critical" },
{ "key": "new-key", "value": "new-value" }
]
Bulk replace (deletes all existing metadata and replaces with provided set; an empty array clears all metadata):
PUT /threat_models/{id}/metadata/bulk
Content-Type: application/json
[
{ "key": "only-key", "value": "only-value" }
]
Error Responses
Metadata operations return structured error responses:
| Status | Condition |
|---|---|
400 Bad Request |
Invalid key format, value too large, HTML injection detected, or empty request body |
404 Not Found |
Parent entity or metadata key not found |
409 Conflict |
Key already exists (on create) |
500 Internal Server Error |
Database or server failure |
See API-Integration for general API error format details.
Storage
Metadata is stored in a dedicated metadata database table with the following columns:
| Column | Type | Description |
|---|---|---|
id |
varchar(36) | Primary key (UUID) |
entity_type |
varchar(50) | Entity type (e.g., threat_model, threat, diagram) |
entity_id |
varchar(36) | UUID of the parent entity |
key |
varchar(256) | Metadata key |
value |
varchar(1024) | Metadata value |
created_at |
timestamp | Creation timestamp |
modified_at |
timestamp | Last modification timestamp |
A unique composite index on (entity_type, entity_id, key) enforces that each key is unique per entity. Additional indexes support lookups by entity, by key, and by key-value combinations.
Advanced Uses
Custom Reporting
Generate reports based on metadata:
- All high-priority threats
- Threat models by compliance requirement
- Items requiring review by date
Integration with External Systems
Map metadata to external systems using well-known keys:
jira-project-- Jira project keyservicenow-ci-- ServiceNow configuration itemrepository-url-- GitHub repository URL
Examples
Example 1: Compliance Tracking
Threat Model Metadata:
compliance: PCI-DSS,SOC2
data-classification: highly-confidential
audit-frequency: quarterly
next-review: 2025-Q2
Threat Metadata:
control-id: PCI-6.5.1
compliance-requirement: PCI-DSS
verification-method: code-review
Example 2: Team Organization
Threat Model Metadata:
team: platform-team
product: payment-service
owner: [email protected]
technical-lead: [email protected]
Diagram Metadata:
subsystem: authentication
responsible-team: security-team
sla: 99.99%
Example 3: Workflow Integration
Threat Metadata:
jira-ticket: SEC-1234
status: in-progress
priority: P1
target-date: 2025-04-01
assigned-to: security-team
Metadata Schema (Recommended)
Define a standard schema for your organization:
# TMI Metadata Schema
## Threat Model Level
- `owner`: Team or person responsible
- `compliance`: Applicable compliance frameworks
- `environment`: dev, staging, production
- `data-classification`: public, internal, confidential, restricted
## Threat Level
- `priority`: P0, P1, P2, P3
- `control-id`: Reference to control framework
- `jira-ticket`: Jira ticket ID
- `assigned-to`: Responsible party
## Diagram Level
- `subsystem`: Which subsystem this represents
- `level`: Context, L0, L1, L2
- `review-status`: draft, in-review, approved
Future Enhancements
Potential future features:
- Metadata validation and schemas
- Required metadata fields
- Metadata templates
- Metadata inheritance
- Filtering and searching entities by metadata values
Next Steps
- Issue-Tracker-Integration -- Link threats to external issue trackers
- API-Integration -- General API integration guide
- Webhook-Integration -- Webhook setup and event handling
Related Topics
- Creating-Your-First-Threat-Model -- Build your first threat model
- Managing-Threats -- Work with threats in TMI
- API-Overview -- TMI API overview