Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

VersionSpecificationTransportContent Format
TAXII 1.xTAXII 1.1.1HTTP POST with XMLSTIX 1.x (XML)
TAXII 2.xTAXII 2.1RESTful JSONSTIX 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

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)

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

  1. 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
  2. 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"
  3. Set up user accounts - Permissions

    • Define accounts in data-config.yaml
    • Assign permissions to collections
  4. Configure the server - Server Configuration

    • Edit taxii.toml for server settings
    • Use environment variables for production

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

AspectDescription
TransportHTTP POST with XML payloads
ContentSTIX 1.x (XML format)
Collection IDname (string identifier)
Collection Tabledata_collections
ConfigurationYAML 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

TypeDescription
DATA_FEEDOne-way data flow (server → client)
DATA_SETBidirectional (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:

  1. Copy template: cp examples/data-config/full.yaml data-config.yaml
  2. 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

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

PropertyDescriptionRequired
idUnique service identifierYes
typeService type (see below)Yes
properties.pathURL endpoint pathYes
properties.descriptionHuman-readable descriptionNo

Service Types

TypeDescription
DISCOVERYLists available services
INBOXReceives content (push)
POLLProvides content (pull)
COLLECTION_MANAGEMENTLists 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

PropertyDescriptionDefault
nameCollection identifier (unique)Required
descriptionHuman-readable descriptionNone
typeDATA_FEED or DATA_SETDATA_FEED
availableIs collection active?true
accept_all_contentAccept any content type?true
service_idsLinked services[]
supported_contentAllowed content bindingsAll

Update Configuration

To update an existing configuration:

  1. Edit data-config.yaml
  2. Run taxii-cli sync data-config.yaml again

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:
  # ...
OptionValuesDefaultDescription
prune_servicestrue/falsefalseDelete services not in config
collections_not_in_configignore/disable/deleteignoreAction for collections not in config
prune_accountstrue/falsefalseDelete accounts not in config

Collection Actions

Collections support three cleanup actions:

ValueBehavior
ignoreLeave untouched (default)
disableSet available=false
deletePermanently delete

Caution

collections_not_in_config: delete permanently 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

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:

PropertyDescription
destination_collectionsCollections 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:

PropertyDescriptionDefault
max_result_countMaximum results per responseUnlimited

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 inbox and poll

URL Endpoints

Services are accessible at their configured paths:

ServiceDefault PathMethod
Discovery/services/discoveryPOST
Inbox/services/inboxPOST
Poll/services/pollPOST
Collection Management/services/collection-managementPOST

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

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 URNDescription
urn:stix.mitre.org:xml:1.0STIX 1.0
urn:stix.mitre.org:xml:1.1.1STIX 1.1.1
urn:stix.mitre.org:xml:1.2STIX 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

  1. Edit data-config.yaml
  2. 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

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

StatusDescription
SUCCESSRequest completed successfully
UNAUTHORIZEDAuthentication failed
NOT_FOUNDCollection or resource not found
DESTINATION_COLLECTION_ERRORInvalid destination collection
INVALID_REQUESTMalformed 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

AspectDescription
TransportRESTful HTTP with JSON payloads
ContentSTIX 2.x (JSON format)
Collection IDUUID (globally unique identifier)
ConfigurationCLI 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 id field)
  • Has a human-readable title
  • May have an optional alias
  • Contains STIX 2.x objects

Important

The collection id (UUID) is the canonical identifier. The title and alias are 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

AspectTAXII 1.xTAXII 2.x
FormatXMLJSON
StyleService-orientedRESTful
OperationsINBOX (push), POLL (pull)POST/GET on endpoints
Collection IDName (string)UUID
Collection Tabledata_collectionsopentaxii_collection
ConfigurationYAML + syncCLI commands
ContentSTIX 1.xSTIX 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

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:

OptionDescription
--titleHuman-readable name (required)
--descriptionOptional description
--defaultMake 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:

OptionDescription
--api-root-idAPI root UUID (required)
--titleCollection title (required)
--descriptionOptional description
--aliasURL-friendly alias (unique within API root)
--publicAllow unauthenticated read access
--public-writeAllow 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.yaml to data-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

TAXII 2.x Collections & API Roots

Understanding the TAXII 2.x resource hierarchy.

API Roots

API roots are top-level groupings for collections.

Properties

PropertyDescription
idUUID identifier
titleHuman-readable name
descriptionOptional description
defaultWhether 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

PropertyTypeDescription
idUUIDThe identifier - use this for permissions
titleStringHuman-readable name (NOT unique)
descriptionStringOptional description
aliasStringURL-friendly name (unique within API root)
is_publicBooleanAllow unauthenticated read access
is_public_writeBooleanAllow 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:

EndpointMethodDescription
/collections/{id}/GETCollection info
/collections/{id}/objects/GETList objects
/collections/{id}/objects/POSTAdd objects
/collections/{id}/objects/{object_id}/GETGet specific object
/collections/{id}/objects/{object_id}/DELETEDelete object
/collections/{id}/manifest/GETObject 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

  1. Use meaningful titles - But remember they’re for humans, not code
  2. Use aliases for stable URLs - If you share URLs with partners
  3. Note UUIDs immediately - Copy them when you create collections
  4. One purpose per collection - Don’t mix unrelated data
  5. Consider API root structure - Group related collections logically

Next Steps

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

ParameterDescription
added_afterOnly objects added after this timestamp
match[id]Filter by STIX ID
match[type]Filter by object type
match[version]Filter by version
limitMaximum objects to return
nextPagination 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

CodeDescription
200Success
201Created (for POST requests)
400Bad request
401Authentication required
403Forbidden (no permission)
404Not found
406Not acceptable (wrong Accept header)
415Unsupported media type
422Unprocessable 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:

  1. Path specified by DARWIS_TAXII_CONFIG env var
  2. ./taxii.toml (current directory)
  3. ./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

VariableTOMLDescription
DARWIS_TAXII_DB_CONNECTIONdatabase.urlPostgreSQL connection string
DARWIS_TAXII_AUTH_SECRETauth.secretJWT signing secret

Server Settings

VariableTOMLDefaultDescription
DARWIS_TAXII_CONFIG-taxii.tomlConfig file path
DARWIS_TAXII_BIND_ADDRESSbind_address0.0.0.0Server bind address
DARWIS_TAXII_PORTport9000Server port
DARWIS_TAXII_DOMAINdomainlocalhost:9000Public domain for URLs
DARWIS_TAXII_SUPPORT_BASIC_AUTHsupport_basic_authtrueEnable HTTP Basic Auth
DARWIS_TAXII_RETURN_SERVER_ERROR_DETAILSreturn_server_error_detailsfalseShow error details

Auth Settings

VariableTOMLDefaultDescription
DARWIS_TAXII_AUTH_SECRETauth.secretRequiredJWT signing secret
DARWIS_TAXII_TOKEN_TTL_SECSauth.token_ttl_secs3600Token lifetime (seconds)

TAXII 1.x Settings

VariableTOMLDefaultDescription
DARWIS_TAXII_SAVE_RAW_INBOX_MESSAGEStaxii1.save_raw_inbox_messagestrueStore original XML
DARWIS_TAXII_XML_PARSER_SUPPORTS_HUGE_TREEtaxii1.xml_parser_supports_huge_treetrueAllow large XML
DARWIS_TAXII_COUNT_BLOCKS_IN_POLL_RESPONSEStaxii1.count_blocks_in_poll_responsesfalseInclude block count
DARWIS_TAXII_UNAUTHORIZED_STATUStaxii1.unauthorized_statusUNAUTHORIZEDAuth failure status

TAXII 2.x Settings

VariableTOMLDefaultDescription
DARWIS_TAXII_TITLEtaxii2.titleDARWIS TAXIIServer title
DARWIS_TAXII_DESCRIPTIONtaxii2.description-Server description
DARWIS_TAXII_CONTACTtaxii2.contact-Contact email
DARWIS_TAXII_PUBLIC_DISCOVERYtaxii2.public_discoverytrueUnauthenticated discovery
DARWIS_TAXII_MAX_CONTENT_LENGTHtaxii2.max_content_length2048Max request body (bytes)
DARWIS_TAXII_ALLOW_CUSTOM_PROPERTIEStaxii2.allow_custom_propertiestrueAllow custom STIX props
DARWIS_TAXII_DEFAULT_PAGINATION_LIMITtaxii2.default_pagination_limit1000Default page size
DARWIS_TAXII_MAX_PAGINATION_LIMITtaxii2.max_pagination_limit1000Maximum page size

Logging

VariableDefaultDescription
RUST_LOGinfoLog 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

  1. Change the auth secret:

    [auth]
    secret = "use-a-long-random-string-at-least-32-characters"
    
  2. Disable error details:

    return_server_error_details = false
    
  3. Use environment variables for secrets:

    export DARWIS_TAXII_AUTH_SECRET="your-secret"
    export DARWIS_TAXII_DB_CONNECTION="postgresql://..."
    

Performance

  1. Increase content length for large bundles:

    [taxii2]
    max_content_length = 104857600  # 100MB
    
  2. 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?

ProtocolIdentifierWhy
TAXII 1.xnameTAXII 1.x has no UUID concept
TAXII 2.xid (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
ValueAccess
readRead-only
modifyRead + 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
ValueAccess
[read]Read-only
[write]Write-only
[read, write]Full access

Format Summary

ProtocolKeyValue FormatExample
TAXII 1.xCollection nameStringmy-collection: modify
TAXII 2.xCollection UUIDList86c1741e-...: [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_collections table
  • TAXII 2.x: Collection UUID must exist in opentaxii_collection table

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”

  1. Check the collection key format:

    • TAXII 1.x: Use collection name
    • TAXII 2.x: Use collection id (UUID)
  2. Verify the UUID is correct:

    taxii-cli collection list --api-root-id <api-root-id>
    
  3. Re-sync configuration:

    taxii-cli sync data-config.yaml
    

Permissions not taking effect

  1. Ensure you ran taxii-cli sync after changing config
  2. Check the account exists: taxii-cli account list
  3. Verify the collection UUID matches exactly

CLI Reference

The taxii-cli command-line tool manages DARWIS TAXII.

Global Options

taxii-cli [OPTIONS] <COMMAND>
OptionDescription
--config <PATH>Path to taxii.toml
--database-url <URL>Database connection (overrides config)
-h, --helpShow help
-V, --versionShow 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]
OptionDescription
--title <TITLE>API root title (required)
--description <DESC>Optional description
--defaultMake 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]
OptionDescription
--api-root-id <UUID>API root UUID (required)
--title <TITLE>Collection title (required)
--description <DESC>Optional description
--alias <ALIAS>URL-friendly alias
--publicAllow unauthenticated read
--public-writeAllow 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 sync command 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]
OptionDescription
-c, --collection <NAME>Collection name(s) (required, repeatable)
--begin <TIMESTAMP>Start of time window (ISO8601)
--end <TIMESTAMP>End of time window (optional)
-m, --with-messagesAlso 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

VariableDescription
DATABASE_URLPostgreSQL connection string
DARWIS_TAXII_CONFIGPath to taxii.toml
DARWIS_TAXII_AUTH_SECRETJWT 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

OptionValuesDefaultDescription
prune_servicestrue/falsefalseDelete services not in config
collections_not_in_configignore/disable/deleteignoreAction for collections not in config
prune_accountstrue/falsefalseDelete accounts not in config

Collection Cleanup Actions

Collections support three actions since they have an “available” flag:

ValueBehavior
ignoreLeave untouched (default)
disableSet available=false
deletePermanently delete

Caution

delete permanently 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
OpenTAXIIDARWIS TAXII
type: inboxtype: INBOX
type: discoverytype: DISCOVERY
type: polltype: POLL
type: collection_managementtype: COLLECTION_MANAGEMENT
addressproperties.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)
domaindomain
support_basic_authsupport_basic_auth
persistence_api.parameters.db_connectiondatabase.url
auth_api.parameters.secretauth.secret
auth_api.parameters.token_ttl_secsauth.token_ttl_secs

Environment Variables

OpenTAXIIDARWIS TAXII
OPENTAXII_DOMAINDARWIS_TAXII_DOMAIN
DATABASE_HOST, DATABASE_NAMEDARWIS_TAXII_DB_CONNECTION
AUTH_SECRETDARWIS_TAXII_AUTH_SECRET

Database Compatibility

DARWIS TAXII uses the same schema as OpenTAXII:

Shared tables:

  • accounts - User authentication
  • services - TAXII 1.x services
  • data_collections - TAXII 1.x collections
  • content_blocks - STIX content
  • inbox_messages - Inbox records
  • subscriptions - TAXII 1.x subscriptions
  • result_sets - Poll result sets

TAXII 2.x tables (prefixed):

  • opentaxii_api_root
  • opentaxii_collection
  • opentaxii_stixobject
  • opentaxii_job
  • opentaxii_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:

  1. Stop DARWIS TAXII
  2. Restore backup if needed: psql -d opentaxii < opentaxii_backup.sql
  3. Start OpenTAXII

The schema is compatible in both directions.

Differences

FeatureOpenTAXIIDARWIS TAXII
LanguagePythonRust
Config formatYAMLTOML (server), YAML (data)
Custom persistencePlugin APINot supported
Separate auth DBSupportedSingle 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"