Skip to content

Configuration Reference

spanforge can be configured at runtime via environment variables, a spanforge.toml file (loaded by spanforge init), or the Python API (spanforge.configure(...)).

Use spanforge.interpolate_env(template) to expand ${VAR} placeholders in configuration strings at runtime.

Environment variables always take the highest precedence and override values set programmatically or in the TOML file.


Core settings

These variables are read at import time by spanforge.config._load_from_env().

VariableTypeDefaultDescription
SPANFORGE_EXPORTERstringconsoleExport backend. Supported values: console, jsonl, sqlite, otlp, otlp-grpc, webhook, datadog, grafana, cloud.
SPANFORGE_ENDPOINTstring(none)Destination URL for the configured exporter (e.g. OTLP collector, webhook URL).
SPANFORGE_ORG_IDstring(none)Organisation / tenant identifier attached to every event. Useful for multi-tenant deployments.
SPANFORGE_SERVICE_NAMEstringunknown-serviceLogical name of the instrumented service.
SPANFORGE_ENVstringproductionDeployment environment tag (e.g. production, staging, development).
SPANFORGE_SERVICE_VERSIONstring0.0.0Semantic version of the instrumented service.
SPANFORGE_SIGNING_KEYstring(none)Base64-encoded HMAC-SHA256 key. When set, every emitted event receives a _sig field as per RFC-0001.
SPANFORGE_ON_EXPORT_ERRORstringwarnBehaviour when an export call fails. Options: warn (log warning), raise (propagate exception), drop (silently discard).
SPANFORGE_SAMPLE_RATEfloat1.0Fraction of events to emit (0.0–1.0). Values outside this range are clamped automatically.
SPANFORGE_ENABLE_TRACE_STOREboolfalseEnable the in-process trace store (required for the /traces HTTP query endpoint and spanforge ui). Accepts 1, true, or yes.
SPANFORGE_ALLOW_PRIVATE_ENDPOINTSboolfalseAllow HTTP-based exporters (Webhook, OTLP, Datadog, Grafana Loki, Cloud) to target loopback or RFC-1918 addresses. For development only — never enable in production as it bypasses SSRF protections (URL validation + DNS resolution check). Accepts 1, true, or yes.

Persistence progression guide

The default exporter (console) writes to stdout and loses all data when the process exits. Here is the recommended upgrade path as your project grows:

StageExporterCommandInfra neededNotes
Dev / localconsoleconfigure()NoneIn-memory; lost on exit. Great for quick iteration.
MVP / solosqliteconfigure(exporter="sqlite", endpoint="./spanforge.db")NoneDurable, queryable, single file. Zero extra dependencies.
Staging / teamjsonlconfigure(exporter="jsonl", endpoint="./events.jsonl")NoneAppend-only, grep-friendly, easy to ship to ELK/Loki later.
Productionotlpconfigure(exporter="otlp", endpoint="http://collector:4318")OTel collectorFull OpenTelemetry pipeline — Grafana, Datadog, Splunk, etc.
Hosted (any stage)cloudconfigure(exporter="cloud")NoneManaged ingest, 30-day retention, dashboard. $29/mo flat.

SQLite — zero-infra persistence

import spanforge
spanforge.configure(exporter="sqlite", endpoint="./spanforge.db")

Events are stored in a local SQLite database and survive process restarts. Query them with any SQLite client:

sqlite3 spanforge.db "SELECT event_type, source, ts FROM events ORDER BY ts DESC LIMIT 20;"

Or in Python:

import sqlite3, json
conn = sqlite3.connect("./spanforge.db")
for row in conn.execute("SELECT payload FROM events WHERE event_type LIKE 'trace.%' LIMIT 5"):
    print(json.loads(row[0])["event_id"])

JSONL — human-readable append-only log

spanforge.configure(exporter="jsonl", endpoint="./spanforge-events.jsonl")

One JSON object per line — easy to grep, jq, or forward to a log aggregator.


Signing & compliance settings (v1.0)

These variables configure the advanced signing features introduced in v1.0.0.

VariableTypeDefaultDescription
SPANFORGE_SIGNING_KEY_CONTEXTstring(none)Environment label (e.g. prod, staging) used as an additional HKDF context when deriving signing keys via derive_key(). Isolates key material per environment.
SPANFORGE_SIGNING_KEY_EXPIRES_ATstring(none)ISO-8601 datetime (e.g. 2025-03-01T00:00:00Z). When set and the key is expired, sign() raises KeyExpiredError. Use check_key_expiry() to inspect status without signing.
SPANFORGE_SIGNING_KEY_MIN_BITSint256Minimum acceptable key strength in bits. validate_key_strength() returns warnings for keys below this threshold.
SPANFORGE_REQUIRE_ORG_IDboolfalseWhen true, AuditStream.write() raises ValueError if an event has no org_id. Useful in multi-tenant deployments. Accepts 1, true, or yes.
SPANFORGE_NO_EGRESSboolfalseBlock all outbound network calls from exporters. When enabled, only local/file-based exporters (console, jsonl) are permitted. Accepts 1, true, or yes.
SPANFORGE_EGRESS_ALLOWLISTstring(none)Comma-separated list of allowed egress hostnames (e.g. otel.example.com,logs.example.com). Only evaluated when SPANFORGE_NO_EGRESS is false.
SPANFORGE_COMPLIANCE_SAMPLINGboolfalseEnable compliance-grade sampling that preserves deterministic reproducibility for audit trails. Accepts 1, true, or yes.

Example — production signing with expiry and egress lockdown

export SPANFORGE_SIGNING_KEY="base64-encoded-key-here"
export SPANFORGE_SIGNING_KEY_CONTEXT="prod"
export SPANFORGE_SIGNING_KEY_EXPIRES_AT="2026-01-01T00:00:00Z"
export SPANFORGE_REQUIRE_ORG_ID=true
export SPANFORGE_EGRESS_ALLOWLIST="otel-collector.internal,logs.example.com"

Alerting settings

Read by spanforge.alerts.AlertConfig.from_env(). At least one channel must be configured for alerts to be dispatched.

VariableTypeDefaultDescription
SPANFORGE_ALERT_SLACK_WEBHOOKstring(none)Slack Incoming Webhook URL for alert notifications.
SPANFORGE_ALERT_TEAMS_WEBHOOKstring(none)Microsoft Teams Incoming Webhook URL.
SPANFORGE_ALERT_PAGERDUTY_KEYstring(none)PagerDuty Events API v2 routing/integration key.
SPANFORGE_ALERT_SMTP_HOSTstring(none)SMTP server hostname for email alerts (e.g. smtp.sendgrid.net).
SPANFORGE_ALERT_SMTP_PORTint587SMTP server port. STARTTLS is used automatically on port 587.
SPANFORGE_ALERT_EMAIL_FROMstringspanforge@localhostSender address used in alert emails.
SPANFORGE_ALERT_EMAIL_TOstring(none)Comma-separated list of recipient addresses (e.g. ops@example.com,oncall@example.com).
SPANFORGE_ALERT_EMAIL_USERNAMEstring(none)SMTP authentication username.
SPANFORGE_ALERT_EMAIL_PASSWORDstring(none)SMTP authentication password. Never commit this to source control — use a secret manager or CI secret.
SPANFORGE_ALERT_COOLDOWN_SECONDSint300Deduplication window in seconds. Alerts with the same key are suppressed for this duration after the first delivery.

Example — Slack alerting

export SPANFORGE_ALERT_SLACK_WEBHOOK="https://hooks.slack.com/services/T.../B.../xxx"
export SPANFORGE_ALERT_COOLDOWN_SECONDS=120
from spanforge.alerts import AlertConfig

config = AlertConfig.from_env()
manager = config.build_manager()
manager.fire("budget_exceeded", "Cost limit reached: $45.20 / $40.00")

Secrets scanning settings

Read by spanforge.secrets.SecretsScanner and spanforge.sdk.secrets.SFSecretsClient. No extra install required — the secrets scanner is part of the core package.

VariableTypeDefaultDescription
SPANFORGE_SECRETS_CONFIDENCE_THRESHOLDfloat0.85Minimum confidence score (0.0–1.0) for a pattern match to be reported. Lower values surface more candidates; raise to reduce false positives.
SPANFORGE_SECRETS_AUTO_BLOCKbooltrueWhen true, SFSecretsClient.scan() raises SFSecretsBlockedError if any zero-tolerance secret type is detected. Accepts 1, true, or yes.
SPANFORGE_SECRETS_ALLOWLISTstring(none)Comma-separated list of known-safe placeholder values to suppress (e.g. YOUR_KEY_HERE,example_token). Case-insensitive.
SPANFORGE_SECRETS_STORE_REDACTEDboolfalseWhen true, the SDK client stores a redacted copy of the scanned text alongside each scan result for audit trail purposes. Accepts 1, true, or yes.
SPANFORGE_SF_SECRETS_ENDPOINTstring(none)Optional remote sf-secrets service endpoint. When set, SFSecretsClient forwards scans to the service for centralised policy enforcement; falls back to local scanning on network error.

Zero-tolerance auto-blocked types

The following secret types always trigger auto-block regardless of threshold when SPANFORGE_SECRETS_AUTO_BLOCK=true:

BEARER_TOKEN, AWS_ACCESS_KEY, GCP_SERVICE_ACCOUNT, PEM_PRIVATE_KEY, SSH_PRIVATE_KEY, HC_API_KEY, SF_API_KEY, GITHUB_PAT, STRIPE_LIVE_KEY, NPM_TOKEN

Example — strict CI/CD configuration

export SPANFORGE_SECRETS_CONFIDENCE_THRESHOLD=0.75
export SPANFORGE_SECRETS_AUTO_BLOCK=true
export SPANFORGE_SECRETS_ALLOWLIST="YOUR_API_KEY_HERE,REPLACE_ME,example_token"

Example — disable auto-block for audit-only mode

export SPANFORGE_SECRETS_AUTO_BLOCK=false
export SPANFORGE_SECRETS_CONFIDENCE_THRESHOLD=0.70

Example — Python API

from spanforge.secrets import SecretsScanner

scanner = SecretsScanner(
    confidence_threshold=0.80,
    allowlist=["YOUR_KEY_HERE", "example_token"],
)
result = scanner.scan(open("config.env").read())
if result.detected:
    print(result.to_sarif())

PII service settings (Phase 3)

Read by spanforge.sdk.pii.SFPIIClient. These variables control the sf-pii integration introduced in Phase 3.

VariableTypeDefaultDescription
SPANFORGE_SF_PII_ENDPOINTstring"" (local Presidio)URL of a remote sf-pii service. When empty, the local Presidio/regex engine is used. Example: https://pii.internal.example.com.
SPANFORGE_PII_ACTION"flag" | "redact" | "block""flag"Pipeline action applied by apply_pipeline_action(). "flag" annotates; "redact" replaces with type labels; "block" raises SFPIIBlockedError.
SPANFORGE_PII_THRESHOLDfloat0.85Minimum confidence score (0–1) for a detected entity to be acted upon.
SPANFORGE_PII_LANGUAGEstring"en"Default ISO 639-1 language for text scanning. Overridden per-call by the language parameter.
SPANFORGE_PII_MAX_DEPTHint10Maximum JSON nesting depth when scanning structured payloads via scan_payload().

Example — flag-only mode (default)

export SPANFORGE_PII_ACTION=flag
export SPANFORGE_PII_THRESHOLD=0.85

Example — strict block on PII detection

export SPANFORGE_PII_ACTION=block
export SPANFORGE_PII_THRESHOLD=0.80

Example — remote sf-pii service

export SPANFORGE_SF_PII_ENDPOINT=https://pii.internal.example.com
export SPANFORGE_PII_ACTION=redact

Example — Python API

from spanforge.sdk import sf_pii

# Action and threshold can also be passed per-call
result = sf_pii.apply_pipeline_action(
    sf_pii.scan_text("Contact alice@example.com"),
    action="redact",
    threshold=0.80,
)

Audit service settings (Phase 4)

Read by spanforge.sdk.audit.SFAuditClient. These variables control the sf-audit service introduced in Phase 4.

VariableTypeDefaultDescription
SPANFORGE_AUDIT_BYOS_PROVIDER"s3" | "azure" | "gcs" | "r2"(none — local)Bring-Your-Own-Storage backend. When set, sf_audit.append() routes records to the specified cloud provider. When unset, records are stored in-process.

BYOS provider values

ValueBackend
s3Amazon S3
azureAzure Blob Storage
gcsGoogle Cloud Storage
r2Cloudflare R2
(unset)Local in-memory store (default)

Example — route to S3

export SPANFORGE_AUDIT_BYOS_PROVIDER=s3

Example — local mode (default, no env var needed)

from spanforge.sdk import sf_audit

result = sf_audit.append(
    {"score": 0.92, "model": "gpt-4o"},
    schema_key="halluccheck.score.v1",
)
print(result.backend)   # "local"

Example — standalone client with custom config

from spanforge.sdk.audit import SFAuditClient
from spanforge.sdk._base import SFClientConfig

client = SFAuditClient(
    SFClientConfig(
        endpoint="https://audit.internal.example.com",
        api_key="...",
        signing_key="base64-key",
        project_id="my-project",
    ),
    strict_schema=True,
    retention_years=7,
    persist_index=True,
    db_path="/var/spanforge/audit_index.db",
)

CEC service settings (Phase 5)

Read by spanforge.sdk.cec.SFCECClient. These variables control the Compliance Evidence Chain service introduced in Phase 5.

VariableTypeDefaultDescription
SPANFORGE_SIGNING_KEYstring(insecure default)HMAC-SHA256 key used to sign bundle manifests. Also shared with sf-audit. If unset or using the insecure default, a warning is logged at client init. Always set this in production.
SPANFORGE_AUDIT_BYOS_PROVIDER"s3" | "azure" | "gcs" | "r2"(none — local)Shared with sf-audit. When set, sf_cec.get_status() reports the active BYOS provider.

Supported framework values

ValueStandard
eu_ai_actEU AI Act (Articles 9, 10, 12, 13, 14, 15)
iso_42001ISO/IEC 42001 AI Management System (Clauses 6.1, 8.3, 9.1, 10)
nist_ai_rmfNIST AI Risk Management Framework (GOVERN, MAP, MEASURE, MANAGE)
iso27001ISO/IEC 27001 Annex A (A.12.4.1–A.12.4.3)
soc2SOC 2 Type II (CC6, CC7, CC9)

Example — build a compliance evidence bundle

from spanforge.sdk import sf_cec

result = sf_cec.build_bundle(
    project_id="my-agent",
    date_range=("2026-01-01", "2026-03-31"),
    frameworks=["eu_ai_act", "soc2"],
)
print(result.zip_path)       # path to the signed ZIP
print(result.hmac_manifest)  # "hmac-sha256:<hex>"

Example — set a production signing key

export SPANFORGE_SIGNING_KEY=$(openssl rand -hex 32)

Example — verify a bundle

from spanforge.sdk import sf_cec

result = sf_cec.verify_bundle("/path/to/bundle.zip")
assert result.overall_valid, result.errors

Observe service settings (Phase 6)

Read by spanforge.sdk.observe.SFObserveClient at construction time.

VariableTypeDefaultDescription
SPANFORGE_OBSERVE_BACKENDstring"local"Export backend. One of local, otlp, datadog, grafana, splunk, elastic.
SPANFORGE_OBSERVE_SAMPLERstring"always_on"Sampling strategy. One of always_on, always_off, parent_based, trace_id_ratio.
SPANFORGE_OBSERVE_SAMPLE_RATEfloat1.0Fraction of spans to export when SPANFORGE_OBSERVE_SAMPLER=trace_id_ratio. Clamped to [0.0, 1.0].
SPANFORGE_ENVstring"production"Value used for the deployment.environment OTel resource attribute on every span.

Backend URLs

When using a remote backend the base URL is taken from the matching SPANFORGE_<BACKEND>_ENDPOINT variable (e.g. SPANFORGE_OTLP_ENDPOINT). The path suffix is appended automatically:

BackendPath appended
otlp/v1/traces
datadog/api/v0.2/traces
grafana/api/v1/push
splunk/services/collector
elastic/_bulk

Example — enable Datadog export with ratio sampling

export SPANFORGE_OBSERVE_BACKEND=datadog
export SPANFORGE_OBSERVE_SAMPLER=trace_id_ratio
export SPANFORGE_OBSERVE_SAMPLE_RATE=0.25
export SPANFORGE_ENV=staging

Gate service settings (Phase 8)

Read by spanforge.sdk.gate.SFGateClient and the spanforge.gate.GateRunner YAML pipeline engine. These variables control artifact retention, PRRI thresholds, HRI threshold, and the rolling detection windows for the trust gate.

VariableTypeDefaultDescription
SPANFORGE_GATE_ARTIFACT_DIRstring.sf-gate/artifactsDirectory where gate artifacts (.sf-gate.json files) are written and retained. Relative paths are resolved from the current working directory.
SPANFORGE_GATE_ARTIFACT_RETENTION_DAYSint90Artifacts older than this many days are eligible for purge via purge_artifacts().
SPANFORGE_GATE_PRRI_RED_THRESHOLDfloat70PRRI score at or above this value is classified as RED (block). Scores below this and at or above the amber boundary are AMBER; scores below amber are GREEN.
SPANFORGE_GATE_HRI_CRITICAL_THRESHOLDfloat0.05Maximum tolerated hallucination risk index (HRI) critical rate (0.0–1.0). If the rate in the observation window exceeds this, the trust gate blocks.
SPANFORGE_GATE_PII_WINDOW_HOURSint24Rolling window in hours for counting PII detection events used by the trust gate.
SPANFORGE_GATE_SECRETS_WINDOW_HOURSint24Rolling window in hours for counting secrets detection events used by the trust gate.

Example — CI/CD environment

export SPANFORGE_GATE_ARTIFACT_DIR=".sf-gate/artifacts"
export SPANFORGE_GATE_ARTIFACT_RETENTION_DAYS=30
export SPANFORGE_GATE_PRRI_RED_THRESHOLD=65
export SPANFORGE_GATE_HRI_CRITICAL_THRESHOLD=0.03
export SPANFORGE_GATE_PII_WINDOW_HOURS=24
export SPANFORGE_GATE_SECRETS_WINDOW_HOURS=24

Example — strict production mode

# Block on PRRI scores ≥ 60, HRI rate ≥ 2%, shorter windows
export SPANFORGE_GATE_PRRI_RED_THRESHOLD=60
export SPANFORGE_GATE_HRI_CRITICAL_THRESHOLD=0.02
export SPANFORGE_GATE_PII_WINDOW_HOURS=6
export SPANFORGE_GATE_SECRETS_WINDOW_HOURS=6

Example — Python API

from spanforge.sdk import sf_gate

sf_gate.configure({
    "artifact_dir": ".sf-gate/artifacts",
    "artifact_retention_days": 30,
    "prri_red_threshold": 65.0,
    "hri_critical_threshold": 0.03,
})

Redis stream settings

Read by spanforge.export.redis_backend.RedisExporter. Requires the spanforge[redis] extra (pip install "spanforge[redis]").

VariableTypeDefaultDescription
SPANFORGE_REDIS_URLstringredis://localhost:6379Redis connection URL. Supports redis://, rediss:// (TLS), and unix:// socket paths.
SPANFORGE_REDIS_STREAM_KEYstringspanforge:eventsRedis Stream key that events are written to via XADD.
SPANFORGE_REDIS_MAX_LENint100000Maximum number of entries in the stream (XADD MAXLEN ~). Older entries are evicted automatically when the limit is reached.
SPANFORGE_REDIS_TTL_SECONDSint0Per-key TTL in seconds. 0 (default) means no TTL is set.

Example — Redis exporter

export SPANFORGE_EXPORTER=redis
export SPANFORGE_REDIS_URL="rediss://my-redis.example.com:6380"
export SPANFORGE_REDIS_STREAM_KEY="prod:spanforge:events"
export SPANFORGE_REDIS_MAX_LEN=500000

Splunk HEC exporter settings

Read by spanforge.export.siem_splunk.SplunkHECExporter.

VariableTypeDefaultDescription
SPANFORGE_SPLUNK_HEC_URLstring(required)Full URL of the Splunk HEC endpoint, e.g. https://splunk.example.com:8088/services/collector/event.
SPANFORGE_SPLUNK_HEC_TOKENstring(required)Splunk HEC authentication token. Never commit to source control.
SPANFORGE_SPLUNK_INDEXstringmainSplunk index to route events to.
SPANFORGE_SPLUNK_SOURCEstringspanforgeSplunk source field on all events.
SPANFORGE_SPLUNK_SOURCETYPEstringspanforge:eventSplunk sourcetype field on all events.
SPANFORGE_SPLUNK_BATCH_SIZEint50Maximum events per HEC HTTP request.
SPANFORGE_SPLUNK_TIMEOUTfloat10.0HTTP request timeout in seconds.

Example — environment-only configuration

export SPANFORGE_SPLUNK_HEC_URL="https://splunk.example.com:8088/services/collector/event"
export SPANFORGE_SPLUNK_HEC_TOKEN="your-hec-token"
export SPANFORGE_SPLUNK_INDEX="llm-compliance"
export SPANFORGE_SPLUNK_BATCH_SIZE=100

Example — Python API

from spanforge.export.siem_splunk import SplunkHECExporter

# Reads all settings from environment variables
with SplunkHECExporter() as exporter:
    exporter.export(event)

# Or pass arguments explicitly
exporter = SplunkHECExporter(
    hec_url="https://splunk:8088/services/collector/event",
    token="your-token",
    index="llm-compliance",
    batch_size=100,
)

Syslog exporter settings

Read by spanforge.export.siem_syslog.SyslogExporter.

VariableTypeDefaultDescription
SPANFORGE_SYSLOG_HOSTstring(required)Syslog receiver hostname or IP address.
SPANFORGE_SYSLOG_PORTint514UDP or TCP port.
SPANFORGE_SYSLOG_TRANSPORTstringudpTransport protocol. Must be udp or tcp.
SPANFORGE_SYSLOG_FORMATstringrfc5424Message format. Must be rfc5424 or cef.
SPANFORGE_SYSLOG_APP_NAMEstringspanforgeSyslog APP-NAME / CEF device product field.
SPANFORGE_SYSLOG_FACILITYint16Syslog facility code (0–23). Default 16 = local0.

Example — RFC 5424 over UDP (default)

export SPANFORGE_SYSLOG_HOST="siem.example.com"

Example — CEF over TCP

export SPANFORGE_SYSLOG_HOST="siem.example.com"
export SPANFORGE_SYSLOG_PORT="6514"
export SPANFORGE_SYSLOG_TRANSPORT="tcp"
export SPANFORGE_SYSLOG_FORMAT="cef"

Example — Python API

from spanforge.export.siem_syslog import SyslogExporter

# RFC 5424 over UDP — reads host from env
exporter = SyslogExporter()
exporter.export(event)

# CEF over TCP — explicit args
exporter = SyslogExporter(
    host="siem.example.com",
    port=6514,
    transport="tcp",
    format="cef",
    facility=16,
)
exporter.export(event)
exporter.close()

Python API

Environment variables are applied automatically at import time. You can also override any setting programmatically:

import spanforge

spanforge.configure(
    service_name="my-service",
    env="production",
    exporter="otlp",
    endpoint="http://otel-collector:4318",
    signing_key="<base64-key>",
    sample_rate=0.5,
    # v1.0 signing & compliance options
    signing_key_context="prod",
    signing_key_expires_at="2026-01-01T00:00:00Z",
    require_org_id=True,
    no_egress=False,
    egress_allowlist=frozenset({"otel-collector.internal"}),
    compliance_sampling=True,
)

spanforge.configure() merges the supplied fields onto the live singleton; any field not listed keeps its current value (env-var-sourced or default).


spanforge.toml

Running spanforge init scaffolds a spanforge.toml file in the current directory. Settings in this file are loaded when the TOML is explicitly referenced via spanforge.configure_from_file("spanforge.toml").

[spanforge]
service_name = "my-service"
env          = "production"
exporter     = "otlp"
endpoint     = "http://otel-collector:4318"
sample_rate  = 1.0

Environment variables always override values from spanforge.toml.


.halluccheck.toml — Integration Config (v2.0.8+)

Phase 9 introduces a unified config block in .halluccheck.toml that bootstraps all 8 SDK services, the service registry, and local fallback from a single file.

Auto-discovery

load_config_file() searches for .halluccheck.toml in:

  1. The path in $SPANFORGE_CONFIG_PATH (if set)
  2. The current working directory
  3. Parent directories (up to filesystem root)

If no file is found, all values fall back to environment variable defaults.

Full config reference

# .halluccheck.toml — Phase 9 Integration Config

[spanforge]
enabled    = true              # Master switch (default: true)
project_id = "my-agent"        # Override: SPANFORGE_PROJECT_ID
endpoint   = "https://api.spanforge.example.com"  # Override: SPANFORGE_ENDPOINT
api_key    = ""                # Override: SPANFORGE_API_KEY (prefer env var)

[spanforge.services]
sf_pii      = true             # Enable PII service
sf_secrets  = true             # Enable secrets scanning service
sf_audit    = true             # Enable audit service
sf_observe  = true             # Enable observability service
sf_alert    = false            # Enable alerting service
sf_identity = false            # Enable identity service
sf_gate     = false            # Enable CI/CD gate service
sf_cec      = false            # Enable CEC (compliance evidence) service
sf_enterprise = false          # Enable enterprise multi-tenancy (Phase 11)
sf_security = false            # Enable security scanning (Phase 11)

[spanforge.local_fallback]
enabled     = true             # Activate fallback when services are unreachable
max_retries = 3                # Retries before fallback activation
timeout_ms  = 2000             # Override: SPANFORGE_FALLBACK_TIMEOUT_MS (default: 5000)

[pii]
threshold   = 0.8              # Override: SPANFORGE_PII_THRESHOLD (default: 0.8)

[secrets]
auto_block  = true             # Override: SPANFORGE_SECRETS_AUTO_BLOCK (default: true)

Environment variable overrides

Environment variables always take precedence over .halluccheck.toml values:

VariableConfig keyDefault
SPANFORGE_ENDPOINTspanforge.endpoint""
SPANFORGE_API_KEYspanforge.api_key""
SPANFORGE_PROJECT_IDspanforge.project_id"default"
SPANFORGE_PII_THRESHOLDpii.threshold0.8
SPANFORGE_SECRETS_AUTO_BLOCKsecrets.auto_blocktrue
SPANFORGE_LOCAL_TOKEN(identity fallback)""
SPANFORGE_FALLBACK_TIMEOUT_MSspanforge.local_fallback.timeout_ms5000

Python API

from spanforge.sdk import load_config_file, validate_config, validate_config_strict

# Load and auto-discover
config = load_config_file()                    # SFConfigBlock

# Load from explicit path
config = load_config_file("path/to/.halluccheck.toml")

# Validate (soft — returns error list)
errors = validate_config(config)
if errors:
    for e in errors:
        print(f"  - {e}")

# Validate (strict — raises SFConfigValidationError)
validate_config_strict(config)

CLI validation

spanforge config validate                          # auto-discover
spanforge config validate --file .halluccheck.toml # explicit path

Exit codes: 0 = valid, 1 = validation errors, 2 = parse/I/O error.

Service Registry

The ServiceRegistry singleton is initialised from the loaded config and tracks health for all enabled services:

from spanforge.sdk import ServiceRegistry

registry = ServiceRegistry.get_instance()
registry.run_startup_check()                   # ping all enabled services
status = registry.status_response()            # {service: {status, latency_ms, ...}}
registry.start_background_checker()            # re-check every 60 s in daemon thread

Local fallback

When local_fallback.enabled = true and a service is unreachable, the SDK automatically delegates to one of 8 local-mode fallback functions:

Fallback functionServiceBehaviour
pii_fallback()sf-piiRegex PII scan via spanforge.redact
secrets_fallback()sf-secretsRegex secrets scan via spanforge.secrets
audit_fallback()sf-auditHMAC-chained JSONL to local file
observe_fallback()sf-observeOTLP JSON to stdout
alert_fallback()sf-alertLog to stderr at WARNING
identity_fallback()sf-identityTrust SPANFORGE_LOCAL_TOKEN env var
gate_fallback()sf-gateLocal gate evaluation via spanforge.gate
cec_fallback()sf-cecWrite CEC bundle to local JSONL file

All fallback functions emit a WARNING log entry when activated.


Security notes

  • Never log or display SPANFORGE_SIGNING_KEY, SPANFORGE_SIGNING_KEY_CONTEXT, SPANFORGE_ALERT_EMAIL_PASSWORD, or SPANFORGE_ALERT_PAGERDUTY_KEY. All sensitive fields are excluded from repr() on the config dataclass.
  • Use a secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault) or your CI/CD platform's secret injection for credentials in production.
  • SPANFORGE_ALLOW_PRIVATE_ENDPOINTS=true disables SSRF protection. Only use it in fully isolated development environments.

Enterprise service settings (Phase 11)

Read by spanforge.sdk.enterprise.SFEnterpriseClient. These variables control multi-tenancy, data residency, encryption, and air-gap mode.

VariableTypeDefaultDescription
SPANFORGE_ENTERPRISE_ENABLEDboolfalseMaster switch for the enterprise subsystem. Accepts 1, true, or yes.
SPANFORGE_ENTERPRISE_FIPSboolfalseEnforce FIPS 140-2 compliant cryptographic algorithms. When true, SFFIPSError is raised for non-FIPS operations.
SPANFORGE_ENTERPRISE_ENCRYPTION_KEYstring(none)Base64-encoded AES-256 key for field-level encryption. Never log this value.
SPANFORGE_ENTERPRISE_AIRGAPboolfalseEnable air-gap mode. When true, all outbound network calls are blocked and local-only operations are enforced.
SPANFORGE_ENTERPRISE_RESIDENCY_REGIONstring"us"Default data residency region for new tenants.

Example — production enterprise configuration

export SPANFORGE_ENTERPRISE_ENABLED=true
export SPANFORGE_ENTERPRISE_FIPS=true
export SPANFORGE_ENTERPRISE_ENCRYPTION_KEY=$(openssl rand -base64 32)
export SPANFORGE_ENTERPRISE_RESIDENCY_REGION=eu

Example — air-gapped deployment

export SPANFORGE_ENTERPRISE_ENABLED=true
export SPANFORGE_ENTERPRISE_AIRGAP=true

Example — Python API

from spanforge.sdk import sf_enterprise

sf_enterprise.configure_airgap(enabled=True)
sf_enterprise.register_tenant(org_id="acme", project_id="agent-1", region="eu")

Security service settings (Phase 11)

Read by spanforge.sdk.security.SFSecurityClient. These variables control OWASP auditing, threat modelling, and dependency scanning.

VariableTypeDefaultDescription
SPANFORGE_SECURITY_OWASP_ENABLEDbooltrueEnable OWASP Top 10 for LLM audit checks.
SPANFORGE_SECURITY_SCAN_ON_STARTUPboolfalseRun a dependency vulnerability scan at SDK init time.
SPANFORGE_SECURITY_SECRETS_IN_LOGSbooltrueEnable secrets-in-logs detection during audit log scans.

Example — strict security

export SPANFORGE_SECURITY_OWASP_ENABLED=true
export SPANFORGE_SECURITY_SCAN_ON_STARTUP=true
export SPANFORGE_SECURITY_SECRETS_IN_LOGS=true

Example — Python API

from spanforge.sdk import sf_security

result = sf_security.run_owasp_audit()
print(result.categories)
print(result.pass_)

scan = sf_security.run_full_scan()
print(scan.dependencies, scan.static_analysis)

Sandbox mode (Phase 12)

Warning: Sandbox mode silently discards all audit records, alerts, and spans. Never enable sandbox mode in production. State stored in sandbox mode is held in-memory and is lost when the process exits. See In-Memory State Behaviour for a full risk analysis and production checklist.

Sandbox mode routes all SDK service calls to in-memory storage with no production side effects. Ideal for tutorials, demos, CI pipelines, and safe experimentation.

VariableTypeDefaultDescription
SPANFORGE_SANDBOXboolfalseEnable sandbox mode. When active, all service clients use in-memory backends. Accepts 1, true, or yes.

TOML configuration

# spanforge.toml
[spanforge]
sandbox = true

Environment variable

export SPANFORGE_SANDBOX=true

Detection in code

from spanforge.sdk._base import _is_sandbox

if _is_sandbox():
    print("Running in sandbox mode — no production side effects")

Note: spanforge doctor displays a warning when sandbox mode is active. It also shows a live Compliance Posture section at the bottom — the current EU AI Act clause pass/fail count drawn from the event store. Run spanforge compliance readiness for a full pre-audit checklist.


Testing mocks (Phase 12)

The spanforge.testing_mocks module provides 11 mock service clients for unit testing without network calls. No configuration is required — use the mock_all_services() context manager in your tests.

from spanforge.testing_mocks import mock_all_services

def test_pipeline():
    with mock_all_services() as mocks:
        run_my_pipeline()
        mocks["sf_pii"].assert_called("scan")

See the full API reference at testing_mocks.


Identity service settings (Phase 13)

Added in: 2.0.13

Read by spanforge.sdk.identity.SFIdentityClient. These variables configure SAML 2.0, SCIM 2.0, and OIDC integrations. All values are optional; omit any protocol you are not using.

VariableTypeDefaultDescription
SPANFORGE_SAML_IDP_METADATA_URLstring(none)URL of your Identity Provider's SAML 2.0 metadata XML document. When set, the SP metadata returned by saml_metadata() will reference the correct IdP.
SPANFORGE_OIDC_PROVIDER_URLstring(none)OIDC provider discovery base URL (e.g. https://login.example.com). Used by oidc_authorize().
SPANFORGE_OIDC_CLIENT_IDstring(none)OAuth 2.0 client ID registered with the OIDC provider.
SPANFORGE_OIDC_CLIENT_SECRETstring(none)OAuth 2.0 client secret. Never log this value. Never commit it to source control. Prefer injecting from a secret manager.
SPANFORGE_SCIM_BASE_URLstring(none)SCIM 2.0 base URL to expose to your IdP provisioner (e.g. https://api.example.com/scim/v2).

Example — Okta SAML + SCIM

export SPANFORGE_SAML_IDP_METADATA_URL=https://your-org.okta.com/app/<app_id>/sso/saml/metadata
export SPANFORGE_SCIM_BASE_URL=https://api.example.com/scim/v2

Example — Azure AD OIDC

export SPANFORGE_OIDC_PROVIDER_URL=https://login.microsoftonline.com/<tenant_id>/v2.0
export SPANFORGE_OIDC_CLIENT_ID=<azure-ad-client-id>
export SPANFORGE_OIDC_CLIENT_SECRET=$(vault kv get -field=secret secret/spanforge/oidc)

Example — Python API

from spanforge.sdk import sf_identity

# SAML SP metadata (expose at GET /saml/metadata)
xml = sf_identity.saml_metadata()

# Initiate OIDC PKCE login
import os
auth_req = sf_identity.oidc_authorize(
    provider_url=os.environ["SPANFORGE_OIDC_PROVIDER_URL"],
    client_id=os.environ["SPANFORGE_OIDC_CLIENT_ID"],
    redirect_uri="https://app.example.com/oidc/callback",
)
# Redirect user to auth_req.authorization_url

See the full API reference at identity.