Introduction
DARWIS TAXII is an implementation of the TAXII (Trusted Automated eXchange of Intelligence Information) protocol, written in Rust. It is designed as a drop-in replacement for EclecticIQ OpenTAXII (Python), maintaining full database compatibility.
Docker Hub: cysecurity/darwis-taxii
What is TAXII?
TAXII is an application protocol for exchanging cyber threat intelligence (CTI) over HTTPS. It defines a set of services and message exchanges for sharing actionable threat information between organizations.
Supported Protocols
DARWIS TAXII supports both major versions of the TAXII specification:
| Version | Specification | Transport | Content Format |
|---|---|---|---|
| TAXII 1.x | TAXII 1.1.1 | HTTP POST with XML | STIX 1.x (XML) |
| TAXII 2.x | TAXII 2.1 | RESTful JSON | STIX 2.x (JSON) |
Key Features
- Dual Protocol Support: Run TAXII 1.x and 2.x simultaneously on the same server
- Database Compatible: Uses the same PostgreSQL schema as OpenTAXII
- Password Compatible: Supports werkzeug (scrypt) password hashes from OpenTAXII
- CLI Management: Command-line interface for administration
Architecture
┌─────────────────────────────────────────────────────────────┐
│ DARWIS TAXII Server │
├─────────────────────────────┬───────────────────────────────┤
│ TAXII 1.x │ TAXII 2.x │
│ ┌───────────────────────┐ │ ┌─────────────────────────┐ │
│ │ Discovery Service │ │ │ /taxii2/ │ │
│ │ Inbox Service │ │ │ /taxii2/{api-root}/ │ │
│ │ Poll Service │ │ │ /taxii2/.../collections │ │
│ │ Collection Management │ │ │ /taxii2/.../objects │ │
│ └───────────────────────┘ │ └─────────────────────────┘ │
│ │ │ │ │
│ data_collections table │ opentaxii_collection table │
│ (separate storage) │ (separate storage) │
├─────────────────────────────┴───────────────────────────────┤
│ PostgreSQL Database │
└─────────────────────────────────────────────────────────────┘
Note
TAXII 1.x and TAXII 2.x use separate collection storage. Collections cannot be shared between protocols.
When to Use Each Protocol
Use TAXII 1.x when:
- Integrating with legacy systems that only support TAXII 1.x
- Working with STIX 1.x XML content
- Maintaining compatibility with older threat intelligence feeds
Use TAXII 2.x when:
- Building new integrations (preferred for new projects)
- Working with STIX 2.x JSON content
- Using modern threat intelligence platforms
Getting Started
- Quick Start - Installation and basic setup
- TAXII 1.x Guide - Configure and use TAXII 1.x services
- TAXII 2.x Guide - Configure and use TAXII 2.x API
- Migration Guide - Migrate from Python OpenTAXII
Quick Start
This guide covers the basic steps to install and run DARWIS TAXII.
Prerequisites
- PostgreSQL 9.4+ (minimal version to ensure compatibility with existing OpenTAXII instances)
- Docker (optional, for containerized deployment)
Option 1: Docker from Docker Hub (Recommended)
Pull and run directly from Docker Hub:
# Pull the latest image
docker pull cysecurity/darwis-taxii:latest
# Create a working directory
mkdir -p darwis-taxii/config && cd darwis-taxii
# Download example configuration files
curl -o config/taxii.toml https://raw.githubusercontent.com/CSPF-Founder/darwis-taxii/main/taxii.example.toml
curl -o config/data-config.yaml https://raw.githubusercontent.com/CSPF-Founder/darwis-taxii/main/examples/data-config/full.yaml
curl -o docker-compose.yml https://raw.githubusercontent.com/CSPF-Founder/darwis-taxii/main/examples/docker/docker-compose.yml
# Edit configuration files as needed (optional)
# - config/taxii.toml: server settings, domain, auth options
# - config/data-config.yaml: services, collections, accounts
# Start the server with PostgreSQL
docker compose up -d
# Sync data configuration
docker compose exec taxii-server ./taxii-cli sync /app/config/data-config.yaml
# Verify it's running
curl http://localhost:9000/taxii2/
Option 2: Docker from Source
# Clone the repository
git clone https://github.com/CSPF-Founder/darwis-taxii.git
cd darwis-taxii/examples/docker
# Create configuration
mkdir -p config
cp ../data-config/full.yaml config/data-config.yaml # Or accounts.yaml for TAXII 2.x only
# Start the server
docker compose up -d
# Verify it's running
curl http://localhost:9000/taxii2/
Option 3: From Source
# Clone and build
git clone https://github.com/CSPF-Founder/darwis-taxii.git
cd darwis-taxii
cargo build --release
# Set up database
export DATABASE_URL="postgresql://user:password@localhost:5432/taxii"
./target/release/taxii-server migrate
# Create configuration
cp taxii.example.toml taxii.toml
# Edit taxii.toml with your settings
# Start the server
./target/release/taxii-server
Verify Installation
Health Check
curl http://localhost:9000/management/health
Response:
{"alive": true}
Test TAXII 2.x Discovery
curl http://localhost:9000/taxii2/
Response:
{
"title": "DARWIS TAXII",
"api_roots": ["http://localhost:9000/taxii2/default/"]
}
Get Authentication Token
curl -X POST http://localhost:9000/management/auth \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "changeme"}'
Response:
{"token": "eyJ..."}
Next Steps
-
Configure TAXII 1.x - TAXII 1.x Setup
- Copy template:
cp examples/data-config/full.yaml data-config.yaml - Sync with
taxii-cli sync data-config.yaml
- Copy template:
-
Configure TAXII 2.x - TAXII 2.x Setup
- Create API roots:
taxii-cli api-root add --title "My Root" --default - Create collections:
taxii-cli collection add --api-root-id <uuid> --title "Intel"
- Create API roots:
-
Set up user accounts - Permissions
- Define accounts in
data-config.yaml - Assign permissions to collections
- Define accounts in
-
Configure the server - Server Configuration
- Edit
taxii.tomlfor server settings - Use environment variables for production
- Edit
Common Commands
# List accounts
taxii-cli account list
# List TAXII 2.x API roots
taxii-cli api-root list
# List collections for an API root
taxii-cli collection list --api-root-id <uuid>
# Sync configuration
taxii-cli sync data-config.yaml
TAXII 1.x Overview
TAXII 1.x is an XML-based protocol for exchanging cyber threat intelligence. It uses a service-oriented architecture with four main service types.
Protocol Characteristics
| Aspect | Description |
|---|---|
| Transport | HTTP POST with XML payloads |
| Content | STIX 1.x (XML format) |
| Collection ID | name (string identifier) |
| Collection Table | data_collections |
| Configuration | YAML file + taxii-cli sync |
Note
TAXII 1.x collections are separate from TAXII 2.x collections. They use different storage and cannot be shared.
Service Architecture
TAXII 1.x organizes threat intelligence exchange through four service types:
┌─────────────────────────────────────────────────────────────┐
│ TAXII 1.x Services │
├────────────────┬────────────────┬────────────────┬──────────┤
│ Discovery │ Inbox │ Poll │ CollMgmt │
│ │ │ │ │
│ List services │ Push content │ Pull content │ List │
│ available │ TO server │ FROM server │ colls │
└────────────────┴────────────────┴────────────────┴──────────┘
│ │ │ │
└────────────────┴────────────────┴──────────────┘
│
┌───────┴───────┐
│ Collections │
│ (by name) │
└───────────────┘
Discovery Service
Provides information about available services. Clients query this endpoint first to learn what services are available.
Inbox Service
Receives threat intelligence content (push model). Clients send STIX content to this service to store it in collections.
Poll Service
Provides threat intelligence content (pull model). Clients request content from collections, optionally filtering by time range.
Collection Management Service
Lists available collections and their properties.
Collections
TAXII 1.x collections store threat intelligence content. Each collection:
- Has a unique
name(the identifier) - Can be linked to multiple services
- Supports content type filtering
- Has availability status
Collection Types
| Type | Description |
|---|---|
| DATA_FEED | One-way data flow (server → client) |
| DATA_SET | Bidirectional (clients can push and pull) |
Content Bindings
Content bindings specify what types of content a collection accepts:
supported_content:
- binding: urn:stix.mitre.org:xml:1.1.1
- binding: urn:stix.mitre.org:xml:1.2
If accept_all_content: true, all content types are accepted.
Configuration Method
TAXII 1.x services and collections are configured via YAML:
- Copy template:
cp examples/data-config/full.yaml data-config.yaml - Apply with
taxii-cli sync data-config.yaml
See TAXII 1.x Setup for detailed configuration.
Permissions
TAXII 1.x permissions use the collection name as the key:
accounts:
- username: analyst
password: secret
permissions:
my-collection: read # Read-only
other-collection: modify # Read + write
See Permissions for details.
Next Steps
- Setup & Configuration - Configure TAXII 1.x services
- Services - Service types in detail
- Collections - Collection configuration
- API Reference - XML endpoint examples
TAXII 1.x Setup & Configuration
TAXII 1.x is configured via YAML and applied using the CLI.
Configuration File
Create a configuration file (e.g., full.yaml) with your services, collections, and accounts.
See examples/data-config/full.yaml for a complete example:
# TAXII 1.x Services
services:
- id: discovery
type: DISCOVERY
properties:
path: /services/discovery
description: Discovery service
- id: inbox
type: INBOX
properties:
path: /services/inbox
description: Inbox service
destination_collections:
- my-collection
- id: poll
type: POLL
properties:
path: /services/poll
description: Poll service
- id: collection-mgmt
type: COLLECTION_MANAGEMENT
properties:
path: /services/collection-management
description: Collection management
# TAXII 1.x Collections
collections:
- name: my-collection
description: Threat intelligence feed
type: DATA_FEED
available: true
accept_all_content: true
service_ids:
- inbox
- poll
- collection-mgmt
supported_content:
- binding: urn:stix.mitre.org:xml:1.1.1
- binding: urn:stix.mitre.org:xml:1.2
# User accounts
accounts:
- username: admin
password: changeme
is_admin: true
- username: analyst
password: secret
permissions:
my-collection: modify # read + write
Apply Configuration
Sync the configuration to the database:
# From the project directory
taxii-cli sync data-config.yaml
# Or with explicit database connection
DATABASE_URL="postgresql://user:pass@localhost/taxii" taxii-cli sync data-config.yaml
Output:
Services synchronized: 4 created, 0 updated, 0 deleted
Collections synchronized: 1 created, 0 updated, 0 disabled
Accounts synchronized: 2 created, 0 updated
Configuration synchronized successfully
Configuration Options
Service Properties
| Property | Description | Required |
|---|---|---|
id | Unique service identifier | Yes |
type | Service type (see below) | Yes |
properties.path | URL endpoint path | Yes |
properties.description | Human-readable description | No |
Service Types
| Type | Description |
|---|---|
DISCOVERY | Lists available services |
INBOX | Receives content (push) |
POLL | Provides content (pull) |
COLLECTION_MANAGEMENT | Lists collections |
Inbox-Specific Properties
- id: inbox
type: INBOX
properties:
path: /services/inbox
destination_collections: # Which collections receive content
- collection-a
- collection-b
Poll-Specific Properties
- id: poll
type: POLL
properties:
path: /services/poll
max_result_count: 100 # Max results per response
Collection Properties
| Property | Description | Default |
|---|---|---|
name | Collection identifier (unique) | Required |
description | Human-readable description | None |
type | DATA_FEED or DATA_SET | DATA_FEED |
available | Is collection active? | true |
accept_all_content | Accept any content type? | true |
service_ids | Linked services | [] |
supported_content | Allowed content bindings | All |
Update Configuration
To update an existing configuration:
- Edit
data-config.yaml - Run
taxii-cli sync data-config.yamlagain
The sync command creates and updates entities. To control what happens to entities not in your config file, use YAML-level options:
Cleanup Options
# At the top of your YAML file
prune_services: false # Delete services not in config
collections_not_in_config: ignore # ignore | disable | delete
prune_accounts: false # Delete accounts not in config
services:
# ...
collections:
# ...
accounts:
# ...
| Option | Values | Default | Description |
|---|---|---|---|
prune_services | true/false | false | Delete services not in config |
collections_not_in_config | ignore/disable/delete | ignore | Action for collections not in config |
prune_accounts | true/false | false | Delete accounts not in config |
Collection Actions
Collections support three cleanup actions:
| Value | Behavior |
|---|---|
ignore | Leave untouched (default) |
disable | Set available=false |
delete | Permanently delete |
Caution
collections_not_in_config: deletepermanently deletes collections and their content.
Verify Configuration
Check services are configured:
# Query discovery service
curl -X POST http://localhost:9000/services/discovery \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<Discovery_Request xmlns="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"/>'
Next Steps
- Services - Detailed service configuration
- Collections - Collection types and content bindings
- API Reference - XML message examples
TAXII 1.x Services
TAXII 1.x uses four service types to organize threat intelligence exchange.
Service Types
Discovery Service
The entry point for TAXII 1.x clients. Returns information about available services.
services:
- id: discovery
type: DISCOVERY
properties:
path: /services/discovery
description: Discovery service
Clients query this service first to learn what other services are available.
Inbox Service
Receives threat intelligence content (push model). Clients send STIX content to store in collections.
services:
- id: inbox
type: INBOX
properties:
path: /services/inbox
description: Inbox service for receiving content
destination_collections:
- collection-a
- collection-b
Properties:
| Property | Description |
|---|---|
destination_collections | Collections that receive inbox content |
When content is pushed to the inbox, it’s stored in the specified destination collections.
Poll Service
Provides threat intelligence content (pull model). Clients request content from collections.
services:
- id: poll
type: POLL
properties:
path: /services/poll
description: Poll service for retrieving content
max_result_count: 100
Properties:
| Property | Description | Default |
|---|---|---|
max_result_count | Maximum results per response | Unlimited |
Clients can filter poll requests by:
- Collection name
- Time range (exclusive_begin_timestamp, inclusive_end_timestamp)
- Content bindings
Collection Management Service
Lists available collections and their properties.
services:
- id: collection-mgmt
type: COLLECTION_MANAGEMENT
properties:
path: /services/collection-management
description: Collection management service
Returns information about collections the client has access to.
Service-Collection Linkage
Collections are linked to services via service_ids:
collections:
- name: my-collection
service_ids:
- inbox # Can receive content via inbox
- poll # Can provide content via poll
- collection-mgmt # Listed in collection management
A collection can be:
- Push-only: Only linked to
inbox - Pull-only: Only linked to
poll - Bidirectional: Linked to both
inboxandpoll
URL Endpoints
Services are accessible at their configured paths:
| Service | Default Path | Method |
|---|---|---|
| Discovery | /services/discovery | POST |
| Inbox | /services/inbox | POST |
| Poll | /services/poll | POST |
| Collection Management | /services/collection-management | POST |
All TAXII 1.x endpoints use HTTP POST with XML payloads.
Multiple Services
You can define multiple services of the same type:
services:
# High-priority inbox
- id: inbox-priority
type: INBOX
properties:
path: /services/inbox-priority
destination_collections:
- priority-intel
# Standard inbox
- id: inbox-standard
type: INBOX
properties:
path: /services/inbox
destination_collections:
- general-intel
Service Discovery Response
When a client queries the discovery service, they receive:
<Discovery_Response xmlns="http://taxii.mitre.org/messages/taxii_xml_binding-1.1">
<Service_Instance service_type="DISCOVERY" available="true">
<Protocol_Binding>urn:taxii.mitre.org:protocol:http:1.0</Protocol_Binding>
<Address>http://localhost:9000/services/discovery</Address>
</Service_Instance>
<Service_Instance service_type="INBOX" available="true">
<Protocol_Binding>urn:taxii.mitre.org:protocol:http:1.0</Protocol_Binding>
<Address>http://localhost:9000/services/inbox</Address>
</Service_Instance>
<!-- ... other services ... -->
</Discovery_Response>
Next Steps
- Collections - Collection configuration
- API Reference - XML message examples
TAXII 1.x Collections
Collections are containers for threat intelligence content in TAXII 1.x.
Collection Identifier
In TAXII 1.x, the collection name is the identifier. There are no UUIDs.
collections:
- name: my-collection # This IS the identifier
description: My threat intelligence
This name is used:
- In poll requests to specify which collection to query
- In inbox messages to specify destination
- In permissions to grant access
Collection Types
DATA_FEED
One-way data flow. Content is produced by the server and consumed by clients.
- name: threat-feed
type: DATA_FEED
service_ids:
- poll # Clients can pull
# No inbox - clients cannot push
Use for:
- External threat feeds
- Read-only intelligence
- Broadcast content
DATA_SET
Bidirectional data flow. Clients can both push and pull content.
- name: shared-intel
type: DATA_SET
service_ids:
- inbox # Clients can push
- poll # Clients can pull
Use for:
- Collaborative intelligence sharing
- Community collections
- Internal threat repositories
Content Bindings
Content bindings restrict what types of content a collection accepts.
Accept All Content
- name: open-collection
accept_all_content: true # Any content type allowed
Restrict Content Types
- name: stix-only
accept_all_content: false
supported_content:
- binding: urn:stix.mitre.org:xml:1.1.1
- binding: urn:stix.mitre.org:xml:1.2
Common Content Bindings
| Binding URN | Description |
|---|---|
urn:stix.mitre.org:xml:1.0 | STIX 1.0 |
urn:stix.mitre.org:xml:1.1.1 | STIX 1.1.1 |
urn:stix.mitre.org:xml:1.2 | STIX 1.2 |
Subtypes
Content bindings can have subtypes:
supported_content:
- binding: urn:stix.mitre.org:xml:1.1.1
subtypes:
- stix.mitre.org:xml:1.1.1:indicator
- stix.mitre.org:xml:1.1.1:ttp
Service Linkage
Collections must be linked to services to be accessible:
collections:
- name: intel-feed
service_ids:
- inbox # Receive via inbox service
- poll # Query via poll service
- collection-mgmt # List via collection management
Access Pattern Examples
Read-only collection:
- name: external-feed
service_ids:
- poll
- collection-mgmt
Write-only collection:
- name: submissions
service_ids:
- inbox
Full access:
- name: shared-intel
service_ids:
- inbox
- poll
- collection-mgmt
Availability
Control whether a collection is active:
- name: my-collection
available: true # Active - accessible via services
- name: archived
available: false # Inactive - not accessible
Setting available: false:
- Hides the collection from discovery
- Rejects inbox/poll requests
- Preserves the data
Full Example
collections:
# Primary threat intelligence feed
- name: threat-intel
description: Curated threat intelligence
type: DATA_FEED
available: true
accept_all_content: false
supported_content:
- binding: urn:stix.mitre.org:xml:1.1.1
- binding: urn:stix.mitre.org:xml:1.2
service_ids:
- poll
- collection-mgmt
# Community sharing collection
- name: community-sharing
description: Community-contributed intelligence
type: DATA_SET
available: true
accept_all_content: true
service_ids:
- inbox
- poll
- collection-mgmt
# Internal submissions
- name: internal-submissions
description: Internal threat submissions
type: DATA_SET
available: true
accept_all_content: true
service_ids:
- inbox
Managing Collections
List Collections
Query the collection management service:
curl -X POST http://localhost:9000/services/collection-management \
-u analyst:password \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<taxii_11:Collection_Information_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"/>'
Update Collections
- Edit
data-config.yaml - Run
taxii-cli sync data-config.yaml
Delete Collections
Remove from config and sync with --force-delete:
taxii-cli sync data-config.yaml --force-delete
Next Steps
- API Reference - XML message examples
- Permissions - Collection access control
TAXII 1.x API Reference
TAXII 1.x uses HTTP POST with XML payloads. All endpoints require authentication unless configured otherwise.
Authentication
# HTTP Basic Auth
curl -X POST http://localhost:9000/services/discovery \
-u username:password \
-H "Content-Type: application/xml" \
-d '...'
# JWT Bearer Token
curl -X POST http://localhost:9000/services/discovery \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/xml" \
-d '...'
Discovery Service
Lists available TAXII services.
Endpoint: POST /services/discovery
Request:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Discovery_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"/>
Response:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Discovery_Response
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"
in_response_to="1">
<taxii_11:Service_Instance service_type="DISCOVERY" available="true">
<taxii_11:Protocol_Binding>urn:taxii.mitre.org:protocol:http:1.0</taxii_11:Protocol_Binding>
<taxii_11:Address>http://localhost:9000/services/discovery</taxii_11:Address>
<taxii_11:Message_Binding>urn:taxii.mitre.org:message:xml:1.1</taxii_11:Message_Binding>
</taxii_11:Service_Instance>
<taxii_11:Service_Instance service_type="INBOX" available="true">
<taxii_11:Protocol_Binding>urn:taxii.mitre.org:protocol:http:1.0</taxii_11:Protocol_Binding>
<taxii_11:Address>http://localhost:9000/services/inbox</taxii_11:Address>
<taxii_11:Message_Binding>urn:taxii.mitre.org:message:xml:1.1</taxii_11:Message_Binding>
</taxii_11:Service_Instance>
<!-- Additional services... -->
</taxii_11:Discovery_Response>
Collection Management
Lists available collections.
Endpoint: POST /services/collection-management
Request:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Collection_Information_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="2"/>
Response:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Collection_Information_Response
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="2"
in_response_to="2">
<taxii_11:Collection collection_name="my-collection"
collection_type="DATA_FEED"
available="true">
<taxii_11:Description>Threat intelligence feed</taxii_11:Description>
<taxii_11:Content_Binding binding_id="urn:stix.mitre.org:xml:1.1.1"/>
<taxii_11:Polling_Service>
<taxii_11:Protocol_Binding>urn:taxii.mitre.org:protocol:http:1.0</taxii_11:Protocol_Binding>
<taxii_11:Address>http://localhost:9000/services/poll</taxii_11:Address>
</taxii_11:Polling_Service>
</taxii_11:Collection>
</taxii_11:Collection_Information_Response>
Poll Service
Retrieves content from a collection.
Endpoint: POST /services/poll
Basic Poll Request
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Poll_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="3"
collection_name="my-collection"/>
Poll with Time Range
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Poll_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="3"
collection_name="my-collection">
<taxii_11:Exclusive_Begin_Timestamp>2024-01-01T00:00:00Z</taxii_11:Exclusive_Begin_Timestamp>
<taxii_11:Inclusive_End_Timestamp>2024-01-31T23:59:59Z</taxii_11:Inclusive_End_Timestamp>
</taxii_11:Poll_Request>
Poll Response
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Poll_Response
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="3"
in_response_to="3"
collection_name="my-collection">
<taxii_11:Content_Block>
<taxii_11:Content_Binding binding_id="urn:stix.mitre.org:xml:1.1.1"/>
<taxii_11:Content>
<!-- STIX content here -->
</taxii_11:Content>
<taxii_11:Timestamp_Label>2024-01-15T10:30:00Z</taxii_11:Timestamp_Label>
</taxii_11:Content_Block>
</taxii_11:Poll_Response>
Inbox Service
Submits content to a collection.
Endpoint: POST /services/inbox
Request:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Inbox_Message
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="4">
<taxii_11:Destination_Collection_Name>my-collection</taxii_11:Destination_Collection_Name>
<taxii_11:Content_Block>
<taxii_11:Content_Binding binding_id="urn:stix.mitre.org:xml:1.1.1"/>
<taxii_11:Content>
<stix:STIX_Package xmlns:stix="http://stix.mitre.org/stix-1"
id="example:Package-1"
version="1.1.1"
timestamp="2024-01-15T10:30:00Z">
<stix:Indicators>
<stix:Indicator id="example:indicator-1" timestamp="2024-01-15T10:30:00Z">
<indicator:Title>Malicious IP Address</indicator:Title>
</stix:Indicator>
</stix:Indicators>
</stix:STIX_Package>
</taxii_11:Content>
</taxii_11:Content_Block>
</taxii_11:Inbox_Message>
Response:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Status_Message
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="4"
in_response_to="4"
status_type="SUCCESS"/>
cURL Examples
Discovery
curl -X POST http://localhost:9000/services/discovery \
-u admin:changeme \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<taxii_11:Discovery_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"/>'
List Collections
curl -X POST http://localhost:9000/services/collection-management \
-u admin:changeme \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<taxii_11:Collection_Information_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"/>'
Poll Collection
curl -X POST http://localhost:9000/services/poll \
-u admin:changeme \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<taxii_11:Poll_Request
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"
collection_name="my-collection"/>'
Error Responses
Errors return a Status_Message with status_type indicating the error:
<?xml version="1.0" encoding="UTF-8"?>
<taxii_11:Status_Message
xmlns:taxii_11="http://taxii.mitre.org/messages/taxii_xml_binding-1.1"
message_id="1"
in_response_to="1"
status_type="UNAUTHORIZED">
<taxii_11:Message>Authentication required</taxii_11:Message>
</taxii_11:Status_Message>
Status Types
| Status | Description |
|---|---|
SUCCESS | Request completed successfully |
UNAUTHORIZED | Authentication failed |
NOT_FOUND | Collection or resource not found |
DESTINATION_COLLECTION_ERROR | Invalid destination collection |
INVALID_REQUEST | Malformed request |
TAXII 1.1 Specification
For complete protocol details, see the TAXII 1.1.1 Specification.
TAXII 2.x Overview
TAXII 2.x is a RESTful JSON API for exchanging cyber threat intelligence. It provides a modern, standards-based approach to threat intelligence sharing.
Protocol Characteristics
| Aspect | Description |
|---|---|
| Transport | RESTful HTTP with JSON payloads |
| Content | STIX 2.x (JSON format) |
| Collection ID | UUID (globally unique identifier) |
| Configuration | CLI commands |
Architecture
TAXII 2.x organizes resources in a hierarchy:
┌──────────────────────────────────────────────────────────────┐
│ TAXII 2.x Server │
│ │
│ Discovery (/taxii2/) │
│ │ │
│ ├── API Root (/taxii2/default/) │
│ │ │ │
│ │ ├── Collections │
│ │ │ ├── Collection A (UUID) │
│ │ │ │ ├── Objects (STIX) │
│ │ │ │ └── Manifest │
│ │ │ └── Collection B (UUID) │
│ │ │ ├── Objects (STIX) │
│ │ │ └── Manifest │
│ │ └── Status (Job tracking) │
│ │ │
│ └── API Root (/taxii2/other-root/) │
│ └── Collections... │
└──────────────────────────────────────────────────────────────┘
Key Concepts
Discovery
The /taxii2/ endpoint provides server information and lists available API roots.
API Roots
API roots are logical groupings of collections. Each API root:
- Has its own URL path (e.g.,
/taxii2/default/) - Can contain multiple collections
- May have different access controls
Collections
Collections store STIX objects. Each collection:
- Has a UUID identifier (the
idfield) - Has a human-readable
title - May have an optional
alias - Contains STIX 2.x objects
Important
The collection
id(UUID) is the canonical identifier. Thetitleandaliasare for display purposes only.
Objects
STIX 2.x objects are JSON documents representing threat intelligence:
- Indicators (IOCs)
- Malware descriptions
- Threat actors
- Attack patterns
- And more…
Collection Identifier
In TAXII 2.x, collections are identified by their UUID:
86c1741e-7e95-4b17-8940-a8f83eb5fe32
This UUID is used:
- In API URLs to access the collection
- In permissions to grant access
- In all programmatic interactions
The title is NOT the identifier:
- Two collections can have the same title
- Titles can change
- Always use the UUID for permissions
Configuration Method
TAXII 2.x is configured via CLI commands (not YAML sync):
# Create an API root
taxii-cli api-root add --title "Intel" --default
# Create a collection
taxii-cli collection add --api-root-id <uuid> --title "Threat Feed"
See TAXII 2.x Setup for detailed configuration.
Permissions
TAXII 2.x permissions use the collection UUID:
accounts:
- username: analyst
password: secret
permissions:
# Use the collection UUID, not title
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
Find collection UUIDs with:
taxii-cli collection list --api-root-id <api-root-uuid>
See Permissions for details.
STIX 2.x Content
TAXII 2.x transports STIX 2.x objects as JSON bundles:
{
"type": "bundle",
"id": "bundle--example",
"objects": [
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"created": "2024-01-15T12:00:00.000Z",
"modified": "2024-01-15T12:00:00.000Z",
"name": "Malicious IP",
"pattern": "[ipv4-addr:value = '198.51.100.1']",
"pattern_type": "stix",
"valid_from": "2024-01-15T12:00:00Z"
}
]
}
Comparison with TAXII 1.x
| Aspect | TAXII 1.x | TAXII 2.x |
|---|---|---|
| Format | XML | JSON |
| Style | Service-oriented | RESTful |
| Operations | INBOX (push), POLL (pull) | POST/GET on endpoints |
| Collection ID | Name (string) | UUID |
| Collection Table | data_collections | opentaxii_collection |
| Configuration | YAML + sync | CLI commands |
| Content | STIX 1.x | STIX 2.x |
Important
Collections are not shared between protocols. A TAXII 1.x client cannot access TAXII 2.x collections and vice versa.
Next Steps
- Setup & Configuration - Create API roots and collections
- Collections & API Roots - Understanding the hierarchy
- API Reference - REST API endpoints
TAXII 2.x Setup & Configuration
TAXII 2.x is configured via CLI commands, not YAML files.
Create API Root
An API root is required before creating collections:
# Create a default API root
taxii-cli api-root add --title "Threat Intelligence" --default
# Create additional API roots
taxii-cli api-root add --title "Internal Intel"
taxii-cli api-root add --title "Partner Sharing"
Options:
| Option | Description |
|---|---|
--title | Human-readable name (required) |
--description | Optional description |
--default | Make this the default API root |
List API Roots
taxii-cli api-root list
Output:
ID Title Default
────────────────────────────────────────────────────────────────────
a1b2c3d4-e5f6-7890-abcd-ef1234567890 Threat Intelligence Yes
b2c3d4e5-f6a7-8901-bcde-f12345678901 Internal Intel No
Create Collections
Collections must belong to an API root:
# Create a collection
taxii-cli collection add \
--api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
--title "Malware Indicators"
# Create with alias (for friendly URLs)
taxii-cli collection add \
--api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
--title "IP Blocklist" \
--alias blocklist
# Create public collection (no auth required for read)
taxii-cli collection add \
--api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
--title "Public Feed" \
--public
Options:
| Option | Description |
|---|---|
--api-root-id | API root UUID (required) |
--title | Collection title (required) |
--description | Optional description |
--alias | URL-friendly alias (unique within API root) |
--public | Allow unauthenticated read access |
--public-write | Allow unauthenticated write access |
List Collections
# List collections for an API root
taxii-cli collection list --api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890
Output:
ID Title Alias Public
──────────────────────────────────────────────────────────────────────────────
86c1741e-7e95-4b17-8940-a8f83eb5fe32 Malware Indicators - No
24574d4d-d29a-4b53-80c0-be454dfac6d5 IP Blocklist blocklist No
f1e2d3c4-b5a6-7890-abcd-ef1234567890 Public Feed - Yes
Important
Note the collection
ID(UUID) - you’ll need this for permissions.
Set Up Permissions
Permissions are configured in data-config.yaml using collection UUIDs:
accounts:
- username: analyst
password: secret
is_admin: false
permissions:
# TAXII 2.x: use collection UUID
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
24574d4d-d29a-4b53-80c0-be454dfac6d5: [read]
Apply with:
taxii-cli sync data-config.yaml
Tip
For TAXII 2.x only, copy
examples/data-config/accounts.yamltodata-config.yaml.
Delete Resources
Delete Collection
taxii-cli collection delete --id 86c1741e-7e95-4b17-8940-a8f83eb5fe32
Delete API Root
# Must delete all collections first
taxii-cli api-root delete --id a1b2c3d4-e5f6-7890-abcd-ef1234567890
Complete Setup Example
# 1. Create API root
taxii-cli api-root add --title "Threat Intel" --default
# Note the API root ID from output
# 2. Create collections
taxii-cli collection add \
--api-root-id <api-root-id> \
--title "IOC Feed" \
--alias iocs
taxii-cli collection add \
--api-root-id <api-root-id> \
--title "Malware Analysis" \
--alias malware
# 3. List to get collection UUIDs
taxii-cli collection list --api-root-id <api-root-id>
# 4. Update data-config.yaml with UUIDs
cat >> data-config.yaml << EOF
accounts:
- username: analyst
password: analyst123
permissions:
<collection-uuid-1>: [read, write]
<collection-uuid-2>: [read]
EOF
# 5. Sync accounts
taxii-cli sync data-config.yaml
Verify Setup
Test the discovery endpoint:
curl http://localhost:9000/taxii2/
Response:
{
"title": "DARWIS TAXII",
"api_roots": [
"http://localhost:9000/taxii2/default/"
]
}
Test collection access:
TOKEN=$(curl -s -X POST http://localhost:9000/management/auth \
-H "Content-Type: application/json" \
-d '{"username": "analyst", "password": "analyst123"}' | jq -r '.token')
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:9000/taxii2/default/collections/"
Next Steps
- Collections & API Roots - Collection properties in detail
- API Reference - REST API endpoints
- Permissions - Access control
TAXII 2.x Collections & API Roots
Understanding the TAXII 2.x resource hierarchy.
API Roots
API roots are top-level groupings for collections.
Properties
| Property | Description |
|---|---|
id | UUID identifier |
title | Human-readable name |
description | Optional description |
default | Whether this is the default API root |
Use Cases
- By Department: “SOC”, “Threat Intel”, “Incident Response”
- By Access Level: “Public”, “Partners”, “Internal”
- By Data Type: “Malware”, “Phishing”, “Network IOCs”
Default API Root
The default API root is accessible at /taxii2/default/:
taxii-cli api-root add --title "Primary Intel" --default
Other API roots use their title (slugified):
- “Threat Intel” →
/taxii2/threat-intel/
Collections
Collections store STIX 2.x objects.
Properties
| Property | Type | Description |
|---|---|---|
id | UUID | The identifier - use this for permissions |
title | String | Human-readable name (NOT unique) |
description | String | Optional description |
alias | String | URL-friendly name (unique within API root) |
is_public | Boolean | Allow unauthenticated read access |
is_public_write | Boolean | Allow unauthenticated write access |
Collection ID vs Title
The id (UUID) is the canonical identifier:
86c1741e-7e95-4b17-8940-a8f83eb5fe32
The title is for display only:
- Multiple collections can have the same title
- Titles can be changed without affecting integrations
- Never use title for permissions
Example:
API Root: "Threat Intel"
├── Collection "IOCs" (id: 86c1741e-...)
├── Collection "IOCs" (id: 24574d4d-...) ← Same title, different collection!
└── Collection "Malware" (id: f1e2d3c4-...)
Using Alias
Alias provides a friendly URL alternative to UUID:
taxii-cli collection add \
--api-root-id <uuid> \
--title "IP Blocklist" \
--alias blocklist
Access options:
- By UUID:
/taxii2/default/collections/86c1741e-.../ - By alias:
/taxii2/default/collections/blocklist/
Note
Alias must be unique within an API root but can duplicate across API roots.
Access Control
Public Collections
Public collections allow unauthenticated access:
# Read-only public
taxii-cli collection add \
--api-root-id <uuid> \
--title "Public Feed" \
--public
# Fully public (not recommended)
taxii-cli collection add \
--api-root-id <uuid> \
--title "Open Submission" \
--public \
--public-write
Permission-Based Access
For non-public collections, configure permissions per user:
accounts:
- username: analyst
permissions:
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read] # Read only
24574d4d-d29a-4b53-80c0-be454dfac6d5: [read, write] # Full access
Collection Endpoints
Each collection provides these endpoints:
| Endpoint | Method | Description |
|---|---|---|
/collections/{id}/ | GET | Collection info |
/collections/{id}/objects/ | GET | List objects |
/collections/{id}/objects/ | POST | Add objects |
/collections/{id}/objects/{object_id}/ | GET | Get specific object |
/collections/{id}/objects/{object_id}/ | DELETE | Delete object |
/collections/{id}/manifest/ | GET | Object metadata only |
Finding Collection UUIDs
Via CLI
taxii-cli collection list --api-root-id <api-root-uuid>
Via API
curl http://localhost:9000/taxii2/default/collections/ \
-H "Authorization: Bearer $TOKEN"
Response:
{
"collections": [
{
"id": "86c1741e-7e95-4b17-8940-a8f83eb5fe32",
"title": "IOC Feed",
"can_read": true,
"can_write": true,
"media_types": ["application/stix+json;version=2.1"]
}
]
}
Collection Management
Update Collection (Not Supported)
Currently, collections cannot be updated after creation. Delete and recreate if changes are needed.
Delete Collection
taxii-cli collection delete --id 86c1741e-7e95-4b17-8940-a8f83eb5fe32
Caution
This deletes all objects in the collection.
Move Objects Between Collections
Not directly supported. Export objects and re-import to new collection.
Best Practices
- Use meaningful titles - But remember they’re for humans, not code
- Use aliases for stable URLs - If you share URLs with partners
- Note UUIDs immediately - Copy them when you create collections
- One purpose per collection - Don’t mix unrelated data
- Consider API root structure - Group related collections logically
Next Steps
- API Reference - REST API examples
- Permissions - Access control details
TAXII 2.x API Reference
TAXII 2.x is a RESTful API using JSON. All responses use the application/taxii+json;version=2.1 content type.
Authentication
# Get JWT token
TOKEN=$(curl -s -X POST http://localhost:9000/management/auth \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "changeme"}' | jq -r '.token')
# Use token in requests
curl -H "Authorization: Bearer $TOKEN" http://localhost:9000/taxii2/
Or use HTTP Basic Auth (if enabled):
curl -u admin:changeme http://localhost:9000/taxii2/
Discovery
Get server information and available API roots.
Endpoint: GET /taxii2/
curl http://localhost:9000/taxii2/
Response:
{
"title": "DARWIS TAXII",
"description": "Threat Intelligence Exchange",
"contact": "security@example.com",
"default": "http://localhost:9000/taxii2/default/",
"api_roots": [
"http://localhost:9000/taxii2/default/",
"http://localhost:9000/taxii2/partner-intel/"
]
}
API Root Information
Get details about an API root.
Endpoint: GET /taxii2/{api-root}/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/
Response:
{
"title": "Default API Root",
"versions": ["application/taxii+json;version=2.1"],
"max_content_length": 104857600
}
List Collections
Get available collections in an API root.
Endpoint: GET /taxii2/{api-root}/collections/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/
Response:
{
"collections": [
{
"id": "86c1741e-7e95-4b17-8940-a8f83eb5fe32",
"title": "IOC Feed",
"description": "Indicators of Compromise",
"alias": "iocs",
"can_read": true,
"can_write": true,
"media_types": ["application/stix+json;version=2.1"]
},
{
"id": "24574d4d-d29a-4b53-80c0-be454dfac6d5",
"title": "Malware Analysis",
"can_read": true,
"can_write": false,
"media_types": ["application/stix+json;version=2.1"]
}
]
}
Get Collection
Get details about a specific collection.
Endpoint: GET /taxii2/{api-root}/collections/{collection-id}/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/86c1741e-7e95-4b17-8940-a8f83eb5fe32/
Get Objects
Retrieve STIX objects from a collection.
Endpoint: GET /taxii2/{api-root}/collections/{collection-id}/objects/
Basic Request
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/taxii+json;version=2.1" \
http://localhost:9000/taxii2/default/collections/86c1741e-7e95-4b17-8940-a8f83eb5fe32/objects/
With Filters
# Filter by object type
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:9000/taxii2/default/collections/<id>/objects/?match[type]=indicator"
# Filter by time range
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:9000/taxii2/default/collections/<id>/objects/?added_after=2024-01-01T00:00:00Z"
# Pagination
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:9000/taxii2/default/collections/<id>/objects/?limit=100"
Query Parameters
| Parameter | Description |
|---|---|
added_after | Only objects added after this timestamp |
match[id] | Filter by STIX ID |
match[type] | Filter by object type |
match[version] | Filter by version |
limit | Maximum objects to return |
next | Pagination cursor |
Response:
{
"more": false,
"objects": [
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"created": "2024-01-15T12:00:00.000Z",
"modified": "2024-01-15T12:00:00.000Z",
"name": "Malicious IP",
"pattern": "[ipv4-addr:value = '198.51.100.1']",
"pattern_type": "stix",
"valid_from": "2024-01-15T12:00:00Z"
}
]
}
Add Objects
Submit STIX objects to a collection.
Endpoint: POST /taxii2/{api-root}/collections/{collection-id}/objects/
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/taxii+json;version=2.1" \
-H "Accept: application/taxii+json;version=2.1" \
http://localhost:9000/taxii2/default/collections/86c1741e-7e95-4b17-8940-a8f83eb5fe32/objects/ \
-d '{
"objects": [
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"created": "2024-01-15T12:00:00.000Z",
"modified": "2024-01-15T12:00:00.000Z",
"name": "Malicious IP Address",
"description": "Known C2 server",
"pattern": "[ipv4-addr:value = '\''198.51.100.1'\'']",
"pattern_type": "stix",
"valid_from": "2024-01-15T12:00:00Z"
}
]
}'
Response:
{
"id": "status--2d086da7-4bdc-4f91-900e-d77486753710",
"status": "complete",
"total_count": 1,
"success_count": 1,
"failure_count": 0,
"pending_count": 0
}
Get Object
Retrieve a specific STIX object.
Endpoint: GET /taxii2/{api-root}/collections/{collection-id}/objects/{object-id}/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/86c1741e-7e95-4b17-8940-a8f83eb5fe32/objects/indicator--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f/
Delete Object
Remove a STIX object from a collection.
Endpoint: DELETE /taxii2/{api-root}/collections/{collection-id}/objects/{object-id}/
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/86c1741e-7e95-4b17-8940-a8f83eb5fe32/objects/indicator--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f/
Get Object Versions
List all versions of an object.
Endpoint: GET /taxii2/{api-root}/collections/{collection-id}/objects/{object-id}/versions/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/<id>/objects/<object-id>/versions/
Response:
{
"more": false,
"versions": [
"2024-01-15T12:00:00.000Z",
"2024-01-16T08:30:00.000Z"
]
}
Get Manifest
Get metadata about objects without full content.
Endpoint: GET /taxii2/{api-root}/collections/{collection-id}/manifest/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/86c1741e-7e95-4b17-8940-a8f83eb5fe32/manifest/
Response:
{
"more": false,
"objects": [
{
"id": "indicator--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f",
"date_added": "2024-01-15T12:00:00.000Z",
"version": "2024-01-15T12:00:00.000Z",
"media_type": "application/stix+json;version=2.1"
}
]
}
Job Status
Check status of async operations.
Endpoint: GET /taxii2/{api-root}/status/{status-id}/
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/status/status--2d086da7-4bdc-4f91-900e-d77486753710/
Error Responses
Errors return a TAXII error object:
{
"title": "Unauthorized",
"description": "Authentication required",
"http_status": "401"
}
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created (for POST requests) |
| 400 | Bad request |
| 401 | Authentication required |
| 403 | Forbidden (no permission) |
| 404 | Not found |
| 406 | Not acceptable (wrong Accept header) |
| 415 | Unsupported media type |
| 422 | Unprocessable entity (invalid STIX) |
Management Endpoints
These endpoints are not part of the TAXII specification but are provided for server management.
Health Check
Endpoint: GET /management/health
curl http://localhost:9000/management/health
Response:
{"alive": true}
Authentication
Endpoint: POST /management/auth
curl -X POST http://localhost:9000/management/auth \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "changeme"}'
Response:
{"token": "eyJ..."}
TAXII 2.1 Specification
For complete protocol details, see the TAXII 2.1 Specification.
Server Configuration
DARWIS TAXII server settings are configured via taxii.toml or environment variables.
Configuration File
The server searches for taxii.toml in this order:
- Path specified by
DARWIS_TAXII_CONFIGenv var ./taxii.toml(current directory)./config/taxii.toml(config subdirectory)
Example Configuration
bind_address = "0.0.0.0"
port = 9000
domain = "localhost:9000"
support_basic_auth = true
return_server_error_details = false
[database]
url = "postgresql://user:password@localhost:5432/taxii"
[auth]
secret = "your-production-secret-change-this"
token_ttl_secs = 3600
[taxii1]
save_raw_inbox_messages = true
xml_parser_supports_huge_tree = true
count_blocks_in_poll_responses = false
unauthorized_status = "UNAUTHORIZED"
[taxii2]
title = "DARWIS TAXII"
description = "Threat Intelligence Exchange"
contact = "security@example.com"
max_content_length = 104857600
public_discovery = true
allow_custom_properties = true
default_pagination_limit = 1000
max_pagination_limit = 1000
Environment Variables
All settings can be overridden via environment variables with the DARWIS_TAXII_ prefix.
Important
Environment variables > TOML config > Defaults
Required Settings
| Variable | TOML | Description |
|---|---|---|
DARWIS_TAXII_DB_CONNECTION | database.url | PostgreSQL connection string |
DARWIS_TAXII_AUTH_SECRET | auth.secret | JWT signing secret |
Server Settings
| Variable | TOML | Default | Description |
|---|---|---|---|
DARWIS_TAXII_CONFIG | - | taxii.toml | Config file path |
DARWIS_TAXII_BIND_ADDRESS | bind_address | 0.0.0.0 | Server bind address |
DARWIS_TAXII_PORT | port | 9000 | Server port |
DARWIS_TAXII_DOMAIN | domain | localhost:9000 | Public domain for URLs |
DARWIS_TAXII_SUPPORT_BASIC_AUTH | support_basic_auth | true | Enable HTTP Basic Auth |
DARWIS_TAXII_RETURN_SERVER_ERROR_DETAILS | return_server_error_details | false | Show error details |
Auth Settings
| Variable | TOML | Default | Description |
|---|---|---|---|
DARWIS_TAXII_AUTH_SECRET | auth.secret | Required | JWT signing secret |
DARWIS_TAXII_TOKEN_TTL_SECS | auth.token_ttl_secs | 3600 | Token lifetime (seconds) |
TAXII 1.x Settings
| Variable | TOML | Default | Description |
|---|---|---|---|
DARWIS_TAXII_SAVE_RAW_INBOX_MESSAGES | taxii1.save_raw_inbox_messages | true | Store original XML |
DARWIS_TAXII_XML_PARSER_SUPPORTS_HUGE_TREE | taxii1.xml_parser_supports_huge_tree | true | Allow large XML |
DARWIS_TAXII_COUNT_BLOCKS_IN_POLL_RESPONSES | taxii1.count_blocks_in_poll_responses | false | Include block count |
DARWIS_TAXII_UNAUTHORIZED_STATUS | taxii1.unauthorized_status | UNAUTHORIZED | Auth failure status |
TAXII 2.x Settings
| Variable | TOML | Default | Description |
|---|---|---|---|
DARWIS_TAXII_TITLE | taxii2.title | DARWIS TAXII | Server title |
DARWIS_TAXII_DESCRIPTION | taxii2.description | - | Server description |
DARWIS_TAXII_CONTACT | taxii2.contact | - | Contact email |
DARWIS_TAXII_PUBLIC_DISCOVERY | taxii2.public_discovery | true | Unauthenticated discovery |
DARWIS_TAXII_MAX_CONTENT_LENGTH | taxii2.max_content_length | 2048 | Max request body (bytes) |
DARWIS_TAXII_ALLOW_CUSTOM_PROPERTIES | taxii2.allow_custom_properties | true | Allow custom STIX props |
DARWIS_TAXII_DEFAULT_PAGINATION_LIMIT | taxii2.default_pagination_limit | 1000 | Default page size |
DARWIS_TAXII_MAX_PAGINATION_LIMIT | taxii2.max_pagination_limit | 1000 | Maximum page size |
Logging
| Variable | Default | Description |
|---|---|---|
RUST_LOG | info | Log level: trace, debug, info, warn, error |
Database Configuration
Connection String Format
postgresql://username:password@host:port/database
Connection Pool
The server maintains a connection pool to PostgreSQL. Pool size is automatically tuned based on available resources.
SSL/TLS
For SSL connections:
postgresql://user:pass@host:5432/db?sslmode=require
Production Recommendations
Security
-
Change the auth secret:
[auth] secret = "use-a-long-random-string-at-least-32-characters" -
Disable error details:
return_server_error_details = false -
Use environment variables for secrets:
export DARWIS_TAXII_AUTH_SECRET="your-secret" export DARWIS_TAXII_DB_CONNECTION="postgresql://..."
Performance
-
Increase content length for large bundles:
[taxii2] max_content_length = 104857600 # 100MB -
Tune pagination:
[taxii2] default_pagination_limit = 100 max_pagination_limit = 1000
Domain Configuration
Set the domain to match your public URL:
domain = "taxii.example.com"
This affects:
- Service URLs in TAXII 1.x discovery
- API root URLs in TAXII 2.x discovery
Docker Configuration
When using Docker, configure via environment variables:
# docker-compose.yml
services:
taxii:
image: darwis-taxii
environment:
- DARWIS_TAXII_DB_CONNECTION=postgresql://user:pass@db:5432/taxii
- DARWIS_TAXII_AUTH_SECRET=your-secret-here
- DARWIS_TAXII_DOMAIN=taxii.example.com
- RUST_LOG=info
ports:
- "9000:9000"
Or mount a config file:
volumes:
- ./taxii.toml:/app/config/taxii.toml
Permissions
Account permissions control access to TAXII collections.
Permission Model
Permissions are defined per-user, per-collection:
accounts:
- username: analyst
password: secret
is_admin: false
permissions:
collection-key: permission-value
Admin Accounts
Accounts with is_admin: true have full access to all collections:
accounts:
- username: admin
password: changeme
is_admin: true
# No permissions needed - admin has full access
Collection Keys
Important
TAXII 1.x and TAXII 2.x collections are completely separate. They are stored in different database tables and cannot be shared between protocols.
The permission key format differs between TAXII versions:
TAXII 1.x: Use Collection Name
TAXII 1.x collections are identified by their name:
# data-config.yaml
collections:
- name: my-collection # ← This is the identifier
type: DATA_FEED
accounts:
- username: analyst
permissions:
my-collection: read # ← Use the name
TAXII 2.x: Use Collection UUID
TAXII 2.x collections are identified by their UUID (id):
accounts:
- username: analyst
permissions:
# Use the UUID, NOT the title
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
Finding collection UUIDs:
taxii-cli collection list --api-root-id <api-root-uuid>
Why Different?
| Protocol | Identifier | Why |
|---|---|---|
| TAXII 1.x | name | TAXII 1.x has no UUID concept |
| TAXII 2.x | id (UUID) | Per spec, UUID is THE identifier |
Common mistake: Using the TAXII 2.x collection title instead of UUID:
# WRONG - title is not the identifier
permissions:
"My Collection": [read, write]
# CORRECT - use UUID
permissions:
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
Permission Values
Critical: The value format determines which protocol the permission applies to.
TAXII 1.x: String Values
TAXII 1.x permissions use string values:
permissions:
my-collection: read # Read-only
other-collection: modify # Read + write
| Value | Access |
|---|---|
read | Read-only |
modify | Read + write |
TAXII 2.x: List Values
TAXII 2.x permissions use list values:
permissions:
86c1741e-...: [read] # Read-only
24574d4d-...: [write] # Write-only (submit without reading)
f8c3e7a2-...: [read, write] # Full access
| Value | Access |
|---|---|
[read] | Read-only |
[write] | Write-only |
[read, write] | Full access |
Format Summary
| Protocol | Key | Value Format | Example |
|---|---|---|---|
| TAXII 1.x | Collection name | String | my-collection: modify |
| TAXII 2.x | Collection UUID | List | 86c1741e-...: [read, write] |
Caution
Using the wrong format will cause validation errors. A UUID with a string value is treated as TAXII 1.x and will fail collection validation.
Mixed Permissions
You can mix TAXII 1.x and 2.x permissions on the same user:
accounts:
- username: analyst
permissions:
# TAXII 1.x collections (by name)
threat-intel: read
incident-data: modify
# TAXII 2.x collections (by UUID)
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
24574d4d-d29a-4b53-80c0-be454dfac6d5: [read]
Complete Example
accounts:
# Admin - full access
- username: admin
password: admin-secret
is_admin: true
# Analyst - mixed TAXII 1.x and 2.x
- username: analyst
password: analyst-secret
is_admin: false
permissions:
# TAXII 1.x collections
legacy-feed: modify
# TAXII 2.x collections
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
# Read-only user
- username: reader
password: reader-secret
is_admin: false
permissions:
legacy-feed: read
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read]
# No permissions (can authenticate but access nothing)
- username: pending
password: pending-secret
is_admin: false
# No permissions defined
Applying Permissions
Permissions are synced from your configuration file:
taxii-cli sync data-config.yaml
The sync command:
- Creates new accounts
- Updates existing account permissions
- Validates all collection references exist (fails if any are missing)
Account Cleanup
By default, accounts not in the config file are left untouched. To delete them:
prune_accounts: true
accounts:
- username: admin
# Only accounts listed here will remain
Collection Validation
Before any changes are made, the sync command validates that all collections referenced in permissions actually exist:
- TAXII 1.x: Collection name must exist in
data_collectionstable - TAXII 2.x: Collection UUID must exist in
opentaxii_collectiontable
If validation fails, no changes are made and an error is shown:
Account 'analyst' references non-existent collections:
- 'unknown-collection' (TAXII 1.x)
- '00000000-0000-0000-0000-000000000000' (TAXII 2.x)
This prevents permissions from referencing collections that don’t exist, which could cause confusing behavior at runtime.
Checking Permissions
Via CLI
taxii-cli account list
Via API
The collection response includes permission info:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/taxii2/default/collections/
Response shows can_read and can_write per collection:
{
"collections": [
{
"id": "86c1741e-...",
"title": "IOC Feed",
"can_read": true,
"can_write": false
}
]
}
Public Collections
TAXII 2.x collections can be made public (no auth required):
# Public read
taxii-cli collection add --api-root-id <id> --title "Public" --public
# Public read + write (use carefully!)
taxii-cli collection add --api-root-id <id> --title "Open" --public --public-write
Public collections bypass permission checks for the specified operations.
Troubleshooting
“User does not have read access”
-
Check the collection key format:
- TAXII 1.x: Use collection
name - TAXII 2.x: Use collection
id(UUID)
- TAXII 1.x: Use collection
-
Verify the UUID is correct:
taxii-cli collection list --api-root-id <api-root-id> -
Re-sync configuration:
taxii-cli sync data-config.yaml
Permissions not taking effect
- Ensure you ran
taxii-cli syncafter changing config - Check the account exists:
taxii-cli account list - Verify the collection UUID matches exactly
CLI Reference
The taxii-cli command-line tool manages DARWIS TAXII.
Global Options
taxii-cli [OPTIONS] <COMMAND>
| Option | Description |
|---|---|
--config <PATH> | Path to taxii.toml |
--database-url <URL> | Database connection (overrides config) |
-h, --help | Show help |
-V, --version | Show version |
Database URL can also be set via DATABASE_URL environment variable.
Commands
sync
Synchronize configuration from YAML file. Manages services, collections, and accounts.
taxii-cli sync <CONFIG_FILE>
Examples:
# Sync configuration
taxii-cli sync data-config.yaml
The sync behavior is controlled via YAML options (not CLI flags). See Sync Configuration below.
api-root
Manage TAXII 2.x API roots.
api-root list
List all API roots.
taxii-cli api-root list
api-root add
Create a new API root.
taxii-cli api-root add [OPTIONS]
| Option | Description |
|---|---|
--title <TITLE> | API root title (required) |
--description <DESC> | Optional description |
--default | Make this the default API root |
Examples:
taxii-cli api-root add --title "Threat Intel" --default
taxii-cli api-root add --title "Partner Sharing" --description "Shared with partners"
collection
Manage TAXII 2.x collections.
collection list
List collections in an API root.
taxii-cli collection list --api-root-id <UUID>
collection add
Create a new collection.
taxii-cli collection add [OPTIONS]
| Option | Description |
|---|---|
--api-root-id <UUID> | API root UUID (required) |
--title <TITLE> | Collection title (required) |
--description <DESC> | Optional description |
--alias <ALIAS> | URL-friendly alias |
--public | Allow unauthenticated read |
--public-write | Allow unauthenticated write |
Examples:
taxii-cli collection add \
--api-root-id a1b2c3d4-... \
--title "IOC Feed" \
--alias iocs
taxii-cli collection add \
--api-root-id a1b2c3d4-... \
--title "Public Intel" \
--public
account
Manage user accounts.
Tip
Accounts are created via the
synccommand with a YAML configuration file. See the examples section below.
account list
List all accounts.
taxii-cli account list
account delete
Delete an account.
taxii-cli account delete --username <NAME>
content
Manage content blocks (TAXII 1.x).
content delete
Delete content blocks from collections.
taxii-cli content delete [OPTIONS]
| Option | Description |
|---|---|
-c, --collection <NAME> | Collection name(s) (required, repeatable) |
--begin <TIMESTAMP> | Start of time window (ISO8601) |
--end <TIMESTAMP> | End of time window (optional) |
-m, --with-messages | Also delete inbox messages |
Examples:
# Delete content from January 2024
taxii-cli content delete \
--collection my-collection \
--begin 2024-01-01T00:00:00Z \
--end 2024-02-01T00:00:00Z
# Delete from multiple collections with messages
taxii-cli content delete \
--collection coll-a \
--collection coll-b \
--begin 2024-01-01T00:00:00Z \
--with-messages
Environment Variables
| Variable | Description |
|---|---|
DATABASE_URL | PostgreSQL connection string |
DARWIS_TAXII_CONFIG | Path to taxii.toml |
DARWIS_TAXII_AUTH_SECRET | JWT signing secret |
Sync Configuration
The sync command behavior is controlled entirely via YAML options, making configuration declarative and version-controllable.
YAML Structure
# Entity cleanup behavior (what happens to entities NOT in this file)
prune_services: false # Delete services not in config (default: false)
collections_not_in_config: ignore # ignore | disable | delete (default: ignore)
prune_accounts: false # Delete accounts not in config (default: false)
# Entity definitions
services:
- id: discovery
type: DISCOVERY
# ...
collections:
- name: my-collection
# ...
accounts:
- username: admin
# ...
Cleanup Options
| Option | Values | Default | Description |
|---|---|---|---|
prune_services | true/false | false | Delete services not in config |
collections_not_in_config | ignore/disable/delete | ignore | Action for collections not in config |
prune_accounts | true/false | false | Delete accounts not in config |
Collection Cleanup Actions
Collections support three actions since they have an “available” flag:
| Value | Behavior |
|---|---|
ignore | Leave untouched (default) |
disable | Set available=false |
delete | Permanently delete |
Caution
deletepermanently removes collections and their content. Use with care.
Common Patterns
Additive sync (default): Only create/update, never delete:
# All cleanup options default to safe values
services:
- id: inbox
# ...
Full declarative control: Config is the source of truth:
prune_services: true
collections_not_in_config: delete
prune_accounts: true
services:
# Only these services will exist
collections:
# Only these collections will exist
accounts:
# Only these accounts will exist
Accounts-only sync: Manage accounts without affecting other entities:
prune_accounts: true
# prune_services and collections_not_in_config default to safe values
accounts:
- username: admin
password: secret
is_admin: true
Collection Reference Validation
When syncing accounts, all collection references in permissions are validated:
- TAXII 1.x permissions: collection name must exist
- TAXII 2.x permissions: collection UUID must exist
If any referenced collection doesn’t exist, the sync fails with an error:
Account 'analyst' references non-existent collections:
- 'invalid-collection' (TAXII 1.x)
- '00000000-0000-0000-0000-000000000000' (TAXII 2.x)
Examples
Complete Setup Workflow
# 1. Create API root
taxii-cli api-root add --title "Intel Hub" --default
# 2. List to get the UUID
taxii-cli api-root list
# ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
# 3. Create collections
taxii-cli collection add \
--api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
--title "Indicators" \
--alias indicators
taxii-cli collection add \
--api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
--title "Malware" \
--alias malware
# 4. List collections to get UUIDs
taxii-cli collection list \
--api-root-id a1b2c3d4-e5f6-7890-abcd-ef1234567890
# 5. Create accounts with permissions
cat > data-config.yaml << EOF
accounts:
- username: analyst
password: analyst123
permissions:
86c1741e-7e95-4b17-8940-a8f83eb5fe32: [read, write]
24574d4d-d29a-4b53-80c0-be454dfac6d5: [read]
EOF
taxii-cli sync data-config.yaml
TAXII 1.x Setup
# Copy template and edit
cp examples/data-config/full.yaml data-config.yaml
# Edit data-config.yaml with your services, collections, accounts
# Apply configuration
taxii-cli sync data-config.yaml
Cleanup Old Data
# Delete content older than 90 days
taxii-cli content delete \
--collection threat-intel \
--begin 1970-01-01T00:00:00Z \
--end $(date -d '90 days ago' --iso-8601=seconds)Z
Migration from OpenTAXII
Migrate from EclecticIQ OpenTAXII (Python) to DARWIS TAXII (Rust).
Important
We recommend testing the migration in a UAT/staging environment first before applying to production. This ensures your specific configuration and data migrate correctly.
Compatibility
DARWIS TAXII is fully compatible with OpenTAXII:
- Same database schema - Point to your existing PostgreSQL database
- Same password hashes - Werkzeug (scrypt) hashes work without changes
- Same data format - All STIX content is preserved
Migration Steps
1. Stop OpenTAXII
docker compose down
# or
systemctl stop opentaxii
2. Backup Database
pg_dump -h localhost -U user -d opentaxii > opentaxii_backup.sql
3. Convert Server Configuration
OpenTAXII (opentaxii.yml):
domain: "localhost:9000"
support_basic_auth: true
persistence_api:
class: opentaxii.persistence.sqldb.SQLDatabaseAPI
parameters:
db_connection: postgresql://user:pass@localhost/opentaxii
auth_api:
class: opentaxii.auth.sqldb.SQLDatabaseAPI
parameters:
db_connection: postgresql://user:pass@localhost/opentaxii
secret: your-secret-key
DARWIS TAXII (taxii.toml):
domain = "localhost:9000"
support_basic_auth = true
[database]
url = "postgresql://user:pass@localhost/opentaxii"
[auth]
secret = "your-secret-key"
4. Convert Data Configuration
Services
OpenTAXII:
services:
- id: inbox
type: inbox
address: /services/inbox
description: Inbox service
DARWIS TAXII:
services:
- id: inbox
type: INBOX # Uppercase
properties: # Nested under properties
path: /services/inbox # 'address' → 'path'
description: Inbox service
| OpenTAXII | DARWIS TAXII |
|---|---|
type: inbox | type: INBOX |
type: discovery | type: DISCOVERY |
type: poll | type: POLL |
type: collection_management | type: COLLECTION_MANAGEMENT |
address | properties.path |
Collections
OpenTAXII:
collections:
- name: my-collection
available: yes
accept_all_content: yes
supported_content:
- urn:stix.mitre.org:xml:1.1.1
DARWIS TAXII:
collections:
- name: my-collection
available: true # 'yes' → 'true'
accept_all_content: true
supported_content:
- binding: urn:stix.mitre.org:xml:1.1.1 # Nested under 'binding'
Accounts
OpenTAXII:
accounts:
- username: admin
password: admin
is_admin: yes
permissions:
my-collection: modify
DARWIS TAXII:
accounts:
- username: admin
password: admin
is_admin: true # 'yes' → 'true'
permissions:
my-collection: modify # Same, or use [read, write]
5. Start DARWIS TAXII
# Migrations run automatically on startup
./taxii-server
# Or with Docker
docker compose up -d
6. Verify Migration
# Check accounts
taxii-cli account list
# Test authentication
curl -X POST http://localhost:9000/management/auth \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "your-password"}'
# Test TAXII 1.x
curl -X POST http://localhost:9000/services/discovery \
-H "Content-Type: application/xml"
# Test TAXII 2.x
curl http://localhost:9000/taxii2/
Configuration Mapping
| OpenTAXII (YAML) | DARWIS TAXII (TOML) |
|---|---|
domain | domain |
support_basic_auth | support_basic_auth |
persistence_api.parameters.db_connection | database.url |
auth_api.parameters.secret | auth.secret |
auth_api.parameters.token_ttl_secs | auth.token_ttl_secs |
Environment Variables
| OpenTAXII | DARWIS TAXII |
|---|---|
OPENTAXII_DOMAIN | DARWIS_TAXII_DOMAIN |
DATABASE_HOST, DATABASE_NAME | DARWIS_TAXII_DB_CONNECTION |
AUTH_SECRET | DARWIS_TAXII_AUTH_SECRET |
Database Compatibility
DARWIS TAXII uses the same schema as OpenTAXII:
Shared tables:
accounts- User authenticationservices- TAXII 1.x servicesdata_collections- TAXII 1.x collectionscontent_blocks- STIX contentinbox_messages- Inbox recordssubscriptions- TAXII 1.x subscriptionsresult_sets- Poll result sets
TAXII 2.x tables (prefixed):
opentaxii_api_rootopentaxii_collectionopentaxii_stixobjectopentaxii_jobopentaxii_job_detail
Password Compatibility
Werkzeug scrypt hashes are fully supported:
scrypt:32768:8:1$salt$hash
Existing accounts work without modification.
Rollback
To rollback to OpenTAXII:
- Stop DARWIS TAXII
- Restore backup if needed:
psql -d opentaxii < opentaxii_backup.sql - Start OpenTAXII
The schema is compatible in both directions.
Differences
| Feature | OpenTAXII | DARWIS TAXII |
|---|---|---|
| Language | Python | Rust |
| Config format | YAML | TOML (server), YAML (data) |
| Custom persistence | Plugin API | Not supported |
| Separate auth DB | Supported | Single DB only |
Troubleshooting
Authentication fails
Ensure auth.secret in taxii.toml matches the secret from OpenTAXII.
Database connection errors
Check connection string format:
postgresql://username:password@host:port/database
Missing TAXII 2.x data
TAXII 2.x resources must be created via CLI:
taxii-cli api-root add --title "Default" --default
taxii-cli collection add --api-root-id <uuid> --title "My Collection"