Files
git.stella-ops.org/docs/modules/concelier/federation-setup.md
StellaOps Bot 17613acf57 feat: add bulk triage view component and related stories
- Exported BulkTriageViewComponent and its related types from findings module.
- Created a new accessibility test suite for score components using axe-core.
- Introduced design tokens for score components to standardize styling.
- Enhanced score breakdown popover for mobile responsiveness with drag handle.
- Added date range selector functionality to score history chart component.
- Implemented unit tests for date range selector in score history chart.
- Created Storybook stories for bulk triage view and score history chart with date range selector.
2025-12-26 01:01:35 +02:00

8.4 KiB

Federation Setup and Operations Guide

This guide covers the setup and operation of StellaOps federation for multi-site vulnerability data synchronization.

Overview

Federation enables secure, cursor-based synchronization of canonical vulnerability advisories between StellaOps sites. It supports:

  • Delta exports: Only changed records since the last cursor are included
  • Air-gap transfers: Bundles can be written to files for offline transfer
  • Multi-site topology: Multiple sites can synchronize independently
  • Cryptographic verification: DSSE signatures ensure bundle authenticity

Architecture

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Site A    │────▶│   Bundle    │────▶│   Site B    │
│  (Export)   │     │   (.zst)    │     │  (Import)   │
└─────────────┘     └─────────────┘     └─────────────┘
                          │
                          ▼
                    ┌───────────┐
                    │  Site C   │
                    │ (Import)  │
                    └───────────┘

Bundle Format

Federation bundles are ZST-compressed TAR archives containing:

File Description
MANIFEST.json Bundle metadata, cursor, counts, hash
canonicals.ndjson Canonical advisories (one per line)
edges.ndjson Source edges linking advisories to sources
deletions.ndjson Withdrawn/deleted advisory IDs
SIGNATURE.json Optional DSSE signature envelope

Configuration

Export Site Configuration

# concelier.yaml
federation:
  enabled: true
  site_id: "us-west-1"                    # Unique site identifier
  export:
    enabled: true
    default_compression_level: 3           # ZST level (1-19)
    sign_bundles: true                     # Sign exported bundles
    max_items_per_bundle: 10000           # Maximum items per export

Import Site Configuration

# concelier.yaml
federation:
  enabled: true
  site_id: "eu-central-1"
  import:
    enabled: true
    skip_signature_verification: false     # NEVER set true in production
    allowed_sites:                         # Trusted site IDs
      - "us-west-1"
      - "ap-south-1"
    conflict_resolution: "prefer_remote"   # prefer_remote | prefer_local | fail
    force_cursor_validation: true          # Reject out-of-order imports

API Endpoints

Export Endpoints

# Export delta bundle since cursor
GET /api/v1/federation/export?since_cursor={cursor}

# Preview export (counts only)
GET /api/v1/federation/export/preview?since_cursor={cursor}

# Get federation status
GET /api/v1/federation/status

Import Endpoints

# Import bundle
POST /api/v1/federation/import
Content-Type: application/zstd

# Validate bundle without importing
POST /api/v1/federation/validate
Content-Type: application/zstd

# List federated sites
GET /api/v1/federation/sites

# Update site policy
PUT /api/v1/federation/sites/{site_id}/policy

CLI Commands

Export Operations

# Export full bundle (no cursor = all data)
feedser bundle export --output bundle.zst

# Export delta since last cursor
feedser bundle export --since-cursor "2025-01-15T10:00:00Z#0001" --output delta.zst

# Preview export without creating bundle
feedser bundle preview --since-cursor "2025-01-15T10:00:00Z#0001"

# Export without signing (testing only)
feedser bundle export --no-sign --output unsigned.zst

Import Operations

# Import bundle
feedser bundle import bundle.zst

# Dry run (validate without importing)
feedser bundle import bundle.zst --dry-run

# Import from stdin (pipe)
cat bundle.zst | feedser bundle import -

# Force import (skip cursor validation)
feedser bundle import bundle.zst --force

Site Management

# List federated sites
feedser sites list

# Show site details
feedser sites show us-west-1

# Enable/disable site
feedser sites enable ap-south-1
feedser sites disable ap-south-1

Cursor Format

Cursors use ISO-8601 timestamp with sequence number:

{ISO-8601 timestamp}#{sequence number}

Examples:
2025-01-15T10:00:00.000Z#0001
2025-01-15T10:00:00.000Z#0002
  • Cursors are site-specific (each site maintains independent cursors)
  • Sequence numbers distinguish concurrent exports
  • Cursors are monotonically increasing within a site

Air-Gap Transfer Workflow

For environments without network connectivity:

# On Source Site (connected to authority)
feedser bundle export --since-cursor "$LAST_CURSOR" --output /media/usb/bundle.zst
feedser bundle preview --since-cursor "$LAST_CURSOR" > /media/usb/manifest.txt

# Transfer media to target site...

# On Target Site (air-gapped)
feedser bundle import /media/usb/bundle.zst --dry-run  # Validate first
feedser bundle import /media/usb/bundle.zst            # Import

Multi-Site Synchronization

Hub-and-Spoke Topology

           ┌─────────────┐
           │   Hub Site  │
           │  (Primary)  │
           └──────┬──────┘
                  │
       ┌──────────┼──────────┐
       ▼          ▼          ▼
 ┌──────────┐ ┌──────────┐ ┌──────────┐
 │  Site A  │ │  Site B  │ │  Site C  │
 │ (Spoke)  │ │ (Spoke)  │ │ (Spoke)  │
 └──────────┘ └──────────┘ └──────────┘

Mesh Topology

Each site can import from multiple sources:

federation:
  import:
    allowed_sites:
      - "hub-primary"
      - "hub-secondary"  # Redundancy

Merge Behavior

Conflict Resolution

When importing, conflicts are resolved based on configuration:

Strategy Behavior
prefer_remote Remote (bundle) value wins (default)
prefer_local Local value preserved
fail Import aborts on any conflict

Merge Actions

Action Description
Created New canonical added
Updated Existing canonical updated
Skipped No change needed (identical)

Verification

Hash Verification

Bundle hash is computed over compressed content:

SHA256(compressed bundle content)

Signature Verification

DSSE envelope contains:

{
  "payloadType": "application/stellaops.federation.bundle+json",
  "payload": "base64(bundle_hash + site_id + cursor)",
  "signatures": [
    {
      "keyId": "signing-key-001",
      "algorithm": "ES256",
      "signature": "base64(signature)"
    }
  ]
}

Monitoring

Key Metrics

  • federation_export_duration_seconds - Export time
  • federation_import_duration_seconds - Import time
  • federation_bundle_size_bytes - Bundle sizes
  • federation_items_processed_total - Items processed by type
  • federation_conflicts_total - Merge conflicts encountered

Health Checks

# Check federation status
curl http://localhost:5000/api/v1/federation/status

# Response
{
  "site_id": "us-west-1",
  "export_enabled": true,
  "import_enabled": true,
  "last_export": "2025-01-15T10:00:00Z",
  "last_import": "2025-01-15T09:30:00Z",
  "sites_synced": 2
}

Troubleshooting

Common Issues

Import fails with "cursor validation failed"

  • Bundle cursor is not after current site cursor
  • Use --force to override (not recommended)
  • Check if bundle was already imported

Signature verification failed

  • Signing key not trusted on target site
  • Key expired or revoked
  • Use --skip-signature for testing only

Large bundle timeout

  • Increase federation.export.timeout
  • Use smaller max_items_per_bundle
  • Stream directly to file

Debug Logging

logging:
  level:
    StellaOps.Concelier.Federation: Debug

Security Considerations

  1. Never skip signature verification in production
  2. Validate allowed_sites whitelist
  3. Use TLS for API endpoints
  4. Rotate signing keys periodically
  5. Audit import events
  6. Monitor for duplicate bundle imports