-
Notifications
You must be signed in to change notification settings - Fork 0
Addon System
This page explains TMI's addon system, which allows administrators to register webhook-based extensions that users can invoke on-demand to enhance threat modeling workflows.
What are addons?
Addons are webhook-based integrations that extend TMI's functionality. Unlike standard webhooks that react to events, addons are user-invoked and can process threat model data on-demand.
Example addons:
- STRIDE analysis automation
- Compliance framework checking
- Threat intelligence enrichment
- Diagram validation
- Custom report generation
- AI-powered threat suggestions
Key characteristics:
- Administrator-registered: Only admins can create/delete addons
- User-invoked: Any authenticated user can trigger addons
- Asynchronous: Long-running operations with status updates
- Webhook-based: External services receive invocation requests
- Rate-limited: Prevents abuse with per-user quotas
User Invokes Addon
↓
TMI Server
↓
Rate Limit Check (Redis)
↓
Create Invocation (Redis)
↓
Addon Worker → HTTP POST → External Service
↓
Process Asynchronously
↓
POST /invocations/{id}/status
↓
Update Status (Redis)
↓
User Polls Status
Components:
- Addons Table (PostgreSQL): Addon registrations
- Invocation Store (Redis): Ephemeral invocation state (7-day TTL)
- Rate Limiter (Redis): Per-user quotas
- Addon Worker: Delivers invocation requests to webhooks
- Status Update Handler: Receives status updates from external services
List available addons:
GET /addons
# Filter by threat model
GET /addons?threat_model_id={threat_model_id}Response:
{
"addons": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "STRIDE Analysis",
"description": "Automated STRIDE threat analysis",
"icon": "material-symbols:security",
"objects": ["threat_model", "asset"],
"threat_model_id": null,
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "7d8f6e5c-4b3a-2190-8765-fedcba987654",
"name": "Compliance Checker",
"description": "Check against NIST/ISO frameworks",
"icon": "material-symbols:shield_lock",
"objects": ["threat_model"],
"created_at": "2025-01-15T11:00:00Z"
}
],
"total": 2
}Trigger an addon to process your threat model:
POST /addons/{addon_id}/invoke
{
"threat_model_id": "threat-model-uuid",
"object_type": "asset", # Optional
"object_id": "asset-uuid", # Optional
"payload": { # Custom data, max 1KB
"analysis_type": "full",
"include_recommendations": true
}
}Response (202 Accepted):
{
"invocation_id": "abc-123-def-456",
"status": "pending",
"created_at": "2025-01-15T12:00:00Z"
}Poll for status updates:
GET /invocations/{invocation_id}Response:
{
"id": "abc-123-def-456",
"addon_id": "550e8400-e29b-41d4-a716-446655440000",
"threat_model_id": "threat-model-uuid",
"status": "in_progress",
"status_percent": 75,
"status_message": "Analyzing assets...",
"created_at": "2025-01-15T12:00:00Z",
"status_updated_at": "2025-01-15T12:02:30Z"
}Status values:
-
pending: Queued for processing -
in_progress: Currently processing -
completed: Successfully finished -
failed: Processing failed
View your invocation history:
GET /invocations
# Filter by status
GET /invocations?status=completed
# Filter by addon
GET /invocations?addon_id={addon_id}Response:
{
"invocations": [
{
"id": "abc-123",
"addon_id": "550e8400-...",
"status": "completed",
"status_percent": 100,
"status_message": "Analysis complete",
"created_at": "2025-01-15T12:00:00Z"
}
],
"total": 10,
"limit": 50,
"offset": 0
}Default quotas per user:
- Active invocations: 1 concurrent (pending or in_progress)
- Hourly rate: 10 invocations per hour
Rate limit error (429 Too Many Requests):
{
"error": "rate_limit_exceeded",
"message": "Maximum of 10 invocations per hour exceeded",
"retry_after": 1234
}Contact your administrator if you need higher limits.
Before creating addons, you need:
-
Administrator privileges (configured in YAML):
administrators: - subject: "[email protected]" subject_type: "user" - subject: "security-team" subject_type: "group"
-
Active webhook subscription:
POST /webhook/subscriptions { "name": "STRIDE Analyzer Service", "url": "https://analyzer.example.com/webhooks/tmi", "events": [], # Addons don't need event subscriptions "secret": "your-hmac-secret" }
Create addon registration:
POST /addons
{
"name": "STRIDE Analysis",
"webhook_id": "webhook-uuid",
"description": "Automated STRIDE threat analysis",
"icon": "material-symbols:security",
"objects": ["threat_model", "asset"],
"threat_model_id": null # Optional: scope to specific threat model
}Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "STRIDE Analysis",
"webhook_id": "webhook-uuid",
"description": "Automated STRIDE threat analysis",
"icon": "material-symbols:security",
"objects": ["threat_model", "asset"],
"created_at": "2025-01-15T10:00:00Z"
}Field validation:
icon (optional):
- Material Symbols:
material-symbols:icon_name - FontAwesome:
fa-solid fa-icon-name - Max length: 60 characters
objects (optional):
- Valid types:
threat_model,diagram,asset,threat,document,note,repository,metadata - Used as UI hint (not enforced on invocation)
threat_model_id (optional):
- Scope addon to specific threat model
- Only visible/invocable within that threat model
DELETE /addons/{addon_id}Response: 204 No Content (success)
Deletion rules:
- Blocked if active invocations exist (status: pending or in_progress)
- Error (409 Conflict): "Cannot delete addon - X active invocations exist"
- Allowed once all invocations complete or fail
- Cascaded when webhook is deleted (ON DELETE CASCADE)
Set custom quotas for specific users:
Database update (no API yet):
INSERT INTO addon_invocation_quotas
(owner_id, max_active_invocations, max_invocations_per_hour)
VALUES
('user-uuid', 5, 50)
ON CONFLICT (owner_id) DO UPDATE
SET max_active_invocations = EXCLUDED.max_active_invocations,
max_invocations_per_hour = EXCLUDED.max_invocations_per_hour;Check quota usage:
-- View custom quotas
SELECT owner_id, max_active_invocations, max_invocations_per_hour
FROM addon_invocation_quotas;
-- Check current usage
SELECT owner_id, COUNT(*) as active_invocations
FROM addon_invocations
WHERE status IN ('pending', 'in_progress')
GROUP BY owner_id;Administrators can view all invocations:
GET /invocations
# Returns all users' invocations for adminsUseful queries:
# Active invocations
GET /invocations?status=in_progress
# Failed invocations (last hour)
GET /invocations?status=failed
# By addon
GET /invocations?addon_id={addon_id}Database queries:
-- Invocation statistics
SELECT addon_id, status, COUNT(*)
FROM addon_invocations
WHERE created_at > NOW() - INTERVAL '24 hours'
GROUP BY addon_id, status;
-- Top users by invocations
SELECT invoked_by, COUNT(*) as count
FROM addon_invocations
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY invoked_by
ORDER BY count DESC
LIMIT 10;Your webhook service must:
- Accept POST requests at the registered URL
- Verify HMAC signatures for security
- Respond with 200 OK within 30 seconds
- Process asynchronously (don't block response)
- Call back with status updates
When a user invokes your addon, you receive:
POST /webhooks/tmi
Content-Type: application/json
X-Webhook-Event: addon_invocation
X-Invocation-Id: abc-123-def-456
X-Addon-Id: 550e8400-e29b-41d4-a716-446655440000
X-Webhook-Signature: sha256=abc123...
User-Agent: TMI-Addon-Worker/1.0
{
"event_type": "addon_invocation",
"invocation_id": "abc-123-def-456",
"addon_id": "550e8400-e29b-41d4-a716-446655440000",
"threat_model_id": "threat-model-uuid",
"object_type": "asset",
"object_id": "asset-uuid",
"timestamp": "2025-01-15T12:00:00Z",
"payload": {
"analysis_type": "full",
"include_recommendations": true
},
"callback_url": "https://api.tmi.dev/invocations/abc-123-def-456/status"
}Always verify HMAC signatures:
import hmac
import hashlib
def verify_signature(payload_bytes, signature, secret):
expected = hmac.new(
secret.encode('utf-8'),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@app.route('/webhooks/tmi', methods=['POST'])
def handle_invocation():
signature = request.headers.get('X-Webhook-Signature')
payload_bytes = request.get_data()
if not verify_signature(payload_bytes, signature, WEBHOOK_SECRET):
return 'Unauthorized', 401
# Process invocation
invocation = request.json
queue.enqueue(process_invocation, invocation)
return '', 200Call back to TMI to update status:
import requests
import hmac
import hashlib
import json
def update_status(invocation_id, status, percent, message=''):
payload = {
'status': status,
'status_percent': percent,
'status_message': message
}
payload_str = json.dumps(payload)
# Generate HMAC signature
signature = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
response = requests.post(
f'https://api.tmi.dev/invocations/{invocation_id}/status',
data=payload_str,
headers={
'Content-Type': 'application/json',
'X-Webhook-Signature': f'sha256={signature}'
}
)
return response.status_code == 200
# Usage during processing
update_status(invocation_id, 'in_progress', 10, 'Starting analysis...')
# ... do work ...
update_status(invocation_id, 'in_progress', 50, 'Analyzing assets...')
# ... do more work ...
update_status(invocation_id, 'completed', 100, 'Analysis complete')Status update validation:
-
status: Must bein_progress,completed, orfailed -
status_percent: Must be 0-100 -
status_message: Optional, max 255 characters
Valid transitions:
-
pending→in_progress→completed -
pending→in_progress→failed - Cannot transition from
completedorfailedback toin_progress
Complete Python example:
from flask import Flask, request
import hmac
import hashlib
import json
import requests
app = Flask(__name__)
WEBHOOK_SECRET = 'your-webhook-secret'
@app.route('/webhooks/tmi', methods=['POST'])
def handle_invocation():
# Verify signature
signature = request.headers.get('X-Webhook-Signature')
payload_bytes = request.get_data()
if not verify_signature(payload_bytes, signature, WEBHOOK_SECRET):
return 'Unauthorized', 401
# Queue for async processing
invocation = request.json
queue.enqueue(process_invocation, invocation)
return '', 200
def process_invocation(invocation):
invocation_id = invocation['invocation_id']
threat_model_id = invocation['threat_model_id']
user_payload = invocation['payload']
try:
# Start processing
update_status(invocation_id, 'in_progress', 0, 'Starting STRIDE analysis')
# Fetch threat model data from TMI API
threat_model = fetch_threat_model(threat_model_id)
# Perform analysis
update_status(invocation_id, 'in_progress', 25, 'Analyzing threats')
threats = analyze_stride(threat_model)
update_status(invocation_id, 'in_progress', 50, 'Analyzing assets')
asset_risks = analyze_assets(threat_model)
update_status(invocation_id, 'in_progress', 75, 'Generating report')
report = generate_report(threats, asset_risks)
# Complete
update_status(invocation_id, 'completed', 100, 'Analysis complete')
except Exception as e:
# Report failure
update_status(invocation_id, 'failed', 0, f'Error: {str(e)}')
def fetch_threat_model(threat_model_id):
response = requests.get(
f'https://api.tmi.dev/api/v1/threat-models/{threat_model_id}',
headers={'Authorization': f'Bearer {TMI_TOKEN}'}
)
response.raise_for_status()
return response.json()
if __name__ == '__main__':
app.run(port=5000)-
Local development:
# Expose local service with ngrok ngrok http 5000 # Use ngrok URL for webhook registration
-
Register webhook and addon:
# Create webhook curl -X POST https://api.tmi.dev/webhook/subscriptions \ -H "Authorization: Bearer $TOKEN" \ -d '{"name":"Test","url":"https://abc.ngrok.io/webhooks/tmi","events":[],"secret":"test"}' # Create addon (admin token required) curl -X POST https://api.tmi.dev/addons \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{"name":"Test Addon","webhook_id":"webhook-uuid"}'
-
Invoke and monitor:
# Invoke addon curl -X POST https://api.tmi.dev/addons/{addon_id}/invoke \ -H "Authorization: Bearer $TOKEN" \ -d '{"threat_model_id":"tm-uuid","payload":{}}' # Check status curl https://api.tmi.dev/invocations/{invocation_id} \ -H "Authorization: Bearer $TOKEN"
- Check status regularly: Poll invocations endpoint for updates
- Handle rate limits: Don't invoke excessively
- Provide context: Use payload to give addon context
- Monitor failures: Check failed invocations for errors
- Vet addon services: Only register trusted webhook services
- Set appropriate quotas: Balance usage and system load
- Monitor performance: Track invocation success rates
- Clean up unused addons: Delete addons no longer needed
- Document addons: Provide users with addon documentation
- Respond quickly: Return 200 OK within 30 seconds
- Process asynchronously: Don't block webhook response
- Update status frequently: Keep users informed of progress
- Handle errors gracefully: Report failures with helpful messages
- Implement timeouts: Don't let processing run indefinitely
- Verify signatures: Always validate HMAC signatures
- Use idempotency: Handle duplicate invocations gracefully
Check:
- Addon exists and is accessible
- Rate limits not exceeded
- Webhook subscription is
active - Webhook service is reachable
Debug:
# Check addon
GET /addons/{addon_id}
# Check webhook status
GET /webhook/subscriptions/{webhook_id}
# Check invocation
GET /invocations/{invocation_id}Causes:
- Invalid HMAC signature on status update
- Invalid status transition
- Invocation expired (7-day TTL)
- Network issues from addon service
Debug:
# Check server logs
grep "invocation" /var/log/tmi/server.log
# Verify signature generation
# Test status update manually
curl -X POST https://api.tmi.dev/invocations/{id}/status \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=..." \
-d '{"status":"completed","status_percent":100}'Investigate:
# Check failed invocations
GET /invocations?status=failed
# Review error messagesCommon causes:
- Webhook service errors
- Timeout processing
- Invalid data from TMI API
- External dependency failures
- Webhook-Integration - Webhook system overview
- Issue-Tracker-Integration - Integration patterns
- REST-API-Reference - Complete API documentation
- Addon Configuration Guide
- Addon Development Guide
- Addon Design Document
- Using TMI for Threat Modeling
- Accessing TMI
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Metadata and Extensions
- Planning Your Deployment
- Deploying TMI Server
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Monitoring and Health
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks