Tiresias Administrator Guide
Version: 1.0 Last Updated: 2026-03-22 Audience: Platform administrators responsible for deploying, configuring, and operating Tiresias
Table of Contents
- Deployment
- SoulKey Management
- Tenant Administration
- Policy Configuration
- Detection & Response Configuration
- SIEM Integration
- Notification Routing
- SoulGate Configuration
- Monitoring & Observability
- Audit & Compliance
- Troubleshooting
1. Deployment
System Requirements
| Component | Minimum Version | Notes |
|---|---|---|
| Python | 3.12+ | Required for SoulAuth and SoulWatch |
| PostgreSQL | 16+ | Primary data store; SQLite available for dev |
| Node.js | 20+ | Required for the Portal frontend |
| Docker | 24+ | Required for containerized deployment |
| Compose | v2.20+ | Docker Compose plugin |
Hardware (minimum per service):
| Service | CPU | Memory | Disk |
|---|---|---|---|
| SoulAuth | 1 vCPU | 1 GB | 10 GB |
| SoulGate | 1 vCPU | 512 MB | 5 GB |
| SoulWatch | 1 vCPU | 512 MB | 5 GB |
| Portal | 0.5 vCPU | 256 MB | 1 GB |
| PostgreSQL | 2 vCPU | 4 GB | 50 GB+ |
| Prometheus | 1 vCPU | 2 GB | 50 GB+ |
Docker Compose Quickstart
The standard docker-compose.yml runs all six services:
| Service | Port | Description |
|---|---|---|
postgres |
5432 | PostgreSQL 16 database |
soulauth |
8000 | Authentication and authorization |
soulwatch |
8001 | Detection, SIEM, and notifications |
soulgate |
8002 | API gateway and proxy |
portal |
3000 | Admin dashboard (React) |
prometheus |
9090 | Metrics collection |
Quick start:
# Clone the repository
git clone https://github.com/your-org/tiresias.git
cd tiresias
# Copy and edit the environment file
cp .env.example .env
# Edit .env with your configuration (see Environment Variables below)
# Generate ES256 key pair for JWT signing
openssl ecparam -genkey -name prime256v1 -noout -out keys/private.pem
openssl ec -in keys/private.pem -pubout -out keys/public.pem
# Start all services
docker compose up -d
# Run database migrations
docker compose exec soulauth alembic upgrade head
# Verify all services are healthy
curl http://localhost:8000/health
curl http://localhost:8001/health
curl http://localhost:8002/health
curl http://localhost:3000/health
Minimal docker-compose.yml example:
version: "3.9"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: tiresias
POSTGRES_USER: tiresias
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U tiresias"]
interval: 5s
retries: 5
soulauth:
image: tiresias/soulauth:latest
ports:
- "8000:8000"
env_file: .env
depends_on:
postgres:
condition: service_healthy
volumes:
- ./keys:/app/keys:ro
soulgate:
image: tiresias/soulgate:latest
ports:
- "8002:8002"
env_file: .env
depends_on:
- soulauth
soulwatch:
image: tiresias/soulwatch:latest
ports:
- "8001:8001"
env_file: .env
depends_on:
- soulauth
portal:
image: tiresias/portal:latest
ports:
- "3000:3000"
environment:
NEXT_PUBLIC_API_URL: http://soulauth:8000
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- promdata:/prometheus
volumes:
pgdata:
promdata:
GKE Production Deployment
For production deployments on Google Kubernetes Engine:
Architecture:
- Cloud SQL for PostgreSQL (HA, automated backups)
- Cloud SQL Auth Proxy as a sidecar in each pod
- 2 replicas per service with rolling update strategy
- Ingress via Google Cloud Load Balancer with managed TLS
Key Kubernetes configuration:
# Deployment template (SoulAuth example)
apiVersion: apps/v1
kind: Deployment
metadata:
name: soulauth
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
containers:
- name: soulauth
image: tiresias/soulauth:latest
ports:
- containerPort: 8000
envFrom:
- secretRef:
name: tiresias-secrets
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 2Gi
- name: cloud-sql-proxy
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2
args:
- "--structured-logs"
- "--auto-iam-authn"
- "PROJECT:REGION:INSTANCE"
securityContext:
runAsNonRoot: true
Deployment steps:
# Create the GKE cluster
gcloud container clusters create tiresias-prod \
--region us-central1 \
--num-nodes 3 \
--machine-type e2-standard-4
# Create Cloud SQL instance
gcloud sql instances create tiresias-db \
--database-version POSTGRES_16 \
--tier db-custom-2-8192 \
--region us-central1 \
--availability-type REGIONAL
# Create Kubernetes secrets
kubectl create secret generic tiresias-secrets \
--from-env-file=.env.production
# Deploy services
kubectl apply -f k8s/
# Verify rollout
kubectl rollout status deployment/soulauth
kubectl rollout status deployment/soulgate
kubectl rollout status deployment/soulwatch
kubectl rollout status deployment/portal
Environment Variables Reference
Core Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
SOULAUTH_MODE |
No | "enterprise" |
"enterprise" for Postgres, "local" for SQLite zero-config |
SOULAUTH_DATABASE_URL |
Yes* | -- | Async PostgreSQL URL (e.g., postgresql+asyncpg://user:pass@host:5432/tiresias) |
SOULAUTH_DATABASE_URL_SYNC |
Yes* | -- | Sync PostgreSQL URL for Alembic (e.g., postgresql+psycopg2://user:pass@host:5432/tiresias) |
SOULAUTH_PUBLIC_URL |
Yes | -- | Public-facing URL (e.g., https://tiresias.network) |
*Required when SOULAUTH_MODE=enterprise.
JWT / Cryptography
| Variable | Required | Default | Description |
|---|---|---|---|
SOULAUTH_JWT_PRIVATE_KEY_PATH |
Yes | -- | Path to ES256 private key PEM file |
SOULAUTH_JWT_PUBLIC_KEY_PATH |
Yes | -- | Path to ES256 public key PEM file |
SOULAUTH_JWT_KID |
Yes | -- | Key ID for key rotation (e.g., kid-2026-03) |
External Services
| Variable | Required | Default | Description |
|---|---|---|---|
RESEND_API_KEY |
No | -- | API key for Resend transactional email |
STRIPE_SECRET_KEY |
No | -- | Stripe secret key for billing |
STRIPE_WEBHOOK_SECRET |
No | -- | Stripe webhook signing secret |
OIDC / SSO
| Variable | Required | Default | Description |
|---|---|---|---|
SOULAUTH_OIDC_ENABLED |
No | false |
Enable OIDC/SSO support |
SOULAUTH_OIDC_SECRET_KEY |
Cond. | -- | Symmetric key for OIDC token encryption (required if OIDC enabled) |
SOULAUTH_OIDC_STATE_SECRET |
Cond. | -- | Secret for OIDC state parameter HMAC (required if OIDC enabled) |
Feature Flags
| Variable | Required | Default | Description |
|---|---|---|---|
SOULAUTH_DETECTION_ENABLED |
No | true |
Enable anomaly detection engine |
SOULAUTH_SIEM_ENABLED |
No | false |
Enable SIEM event forwarding |
SOULAUTH_NOTIFICATIONS_ENABLED |
No | true |
Enable alert notification routing |
Monitoring
| Variable | Required | Default | Description |
|---|---|---|---|
METRICS_AUTH_TOKEN |
No | -- | Bearer token for Prometheus scrape endpoint |
Database Tuning
| Variable | Required | Default | Description |
|---|---|---|---|
SOULAUTH_DB_POOL_SIZE |
No | 10 |
SQLAlchemy connection pool size |
SOULAUTH_DB_MAX_OVERFLOW |
No | 20 |
Max overflow connections beyond pool size |
SOULAUTH_DB_POOL_TIMEOUT |
No | 30 |
Seconds to wait for a connection from the pool |
Database Migrations
Tiresias uses Alembic for database schema management.
# Apply all pending migrations
alembic upgrade head
# Check current migration revision
alembic current
# Generate a new migration after model changes
alembic revision --autogenerate -m "description of change"
# Downgrade one revision (use with caution in production)
alembic downgrade -1
# View migration history
alembic history --verbose
Production migration checklist:
- Back up the database before applying migrations
- Run migrations during a maintenance window
- Test migrations against a staging database first
- Monitor application logs after migration for errors
- Verify with
alembic currentthat the revision matches expectations
2. SoulKey Management
SoulKeys are the primary credentials for agent authentication. Each key is bound to a specific tenant and persona.
Issuing Keys
curl -X POST https://tiresias.network/v1/soulauth/admin/soulkeys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"persona_id": "oracle-research-01",
"description": "Oracle research agent - production",
"expires_at": "2027-03-22T00:00:00Z"
}'
Response (shown once, never retrievable again):
{
"soulkey_id": "sk_id_a1b2c3d4",
"raw_key": "sk_agent_saluca_oracle-research-01_e4f7a8b3c1d9e0f2a5b7c8d1e3f4a6b7",
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"persona_id": "oracle-research-01",
"status": "active",
"created_at": "2026-03-22T12:00:00Z",
"expires_at": "2027-03-22T00:00:00Z"
}
Key Format
SoulKeys follow a structured format for auditability:
sk_agent_<tenant_short>_<persona_slug>_<hex32>
| Segment | Description | Example |
|---|---|---|
sk_agent |
Fixed prefix | sk_agent |
<tenant_short> |
Abbreviated tenant identifier | saluca |
<persona_slug> |
Persona identifier slug | oracle-research-01 |
<hex32> |
32-character hex random string | e4f7a8b3c1d9e0f2a5b7c8d1e3f4a6b7 |
Storage Security
- The raw key is displayed exactly once at creation time
- Only the SHA-512 hash of the key is stored in the database
- There is no mechanism to recover a lost raw key; a new key must be issued
- All key operations are recorded in the tamper-evident audit log
Key Lifecycle
SoulKeys follow a strict state machine:
suspend
active ──────────────► suspended
│ │
│ │ reinstate
│ │
│ revoke ▼
├──────────────────► revoked (terminal)
│ ▲
│ │ revoke
│ │
└──── expired ─────► auto-revoked (terminal)
(auto)
| Transition | Reversible | API Call |
|---|---|---|
| active -> suspended | Yes | PATCH /v1/soulauth/admin/soulkeys/{id}/suspend |
| suspended -> active | Yes | PATCH /v1/soulauth/admin/soulkeys/{id}/reinstate |
| active -> revoked | No | DELETE /v1/soulauth/admin/soulkeys/{id} |
| suspended -> revoked | No | DELETE /v1/soulauth/admin/soulkeys/{id} |
| active -> expired | Automatic | Triggered by expires_at timestamp |
Suspend a key:
curl -X PATCH https://tiresias.network/v1/soulauth/admin/soulkeys/{soulkey_id}/suspend \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason": "Investigating anomalous access pattern"}'
Revoke a key (permanent):
curl -X DELETE https://tiresias.network/v1/soulauth/admin/soulkeys/{soulkey_id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason": "Key compromised - rotating credentials"}'
Key Rotation Procedure
- Issue a new key for the same tenant and persona
- Distribute the new key to all consuming agents/services
- Verify the new key is in use (monitor auth logs for the old key ID)
- Suspend the old key (grace period for stragglers)
- Revoke the old key after confirming zero usage
# Step 1: Issue replacement key
curl -X POST https://tiresias.network/v1/soulauth/admin/soulkeys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"persona_id": "oracle-research-01",
"description": "Oracle research agent - rotated 2026-03-22"
}'
# Step 3: Check old key usage (last 24h)
curl "https://tiresias.network/v1/soulauth/admin/audit?soulkey_id={old_key_id}&since=24h" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Step 4: Suspend old key
curl -X PATCH https://tiresias.network/v1/soulauth/admin/soulkeys/{old_key_id}/suspend \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"reason": "Rotation - new key issued"}'
# Step 5: Revoke old key
curl -X DELETE https://tiresias.network/v1/soulauth/admin/soulkeys/{old_key_id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"reason": "Rotation complete"}'
3. Tenant Administration
Creating Tenants
curl -X POST https://tiresias.network/v1/soulauth/admin/tenants \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"slug": "acme",
"tier": "pro",
"contact_email": "admin@acme.com",
"settings": {
"max_agents": 50,
"max_keys_per_persona": 3,
"audit_retention_days": 30
}
}'
Tier System
| Feature | Community (Free) | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Max Agents | 5 | 25 | 100 | Unlimited |
| Max SoulKeys | 10 | 50 | 500 | Unlimited |
| Audit Retention | 1 day | 7 days | 30 days | 90 days |
| Policy Sync | Manual | 5 min | 1 min | 30 sec |
| Detection Engine | Basic | Standard | Advanced | Advanced + Custom |
| SIEM Integration | -- | -- | 1 destination | Unlimited |
| Notifications | Email only | Email + Slack | All sinks | All sinks + custom |
| SSO / OIDC | -- | -- | Yes | Yes |
| SLA | Best effort | 99.5% | 99.9% | 99.99% |
| Support | Community | Priority | Dedicated |
Updating a Tenant
curl -X PATCH https://tiresias.network/v1/soulauth/admin/tenants/{tenant_id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tier": "enterprise",
"settings": {
"max_agents": -1,
"audit_retention_days": 90
}
}'
Tenant Isolation
All database queries are filtered by tenant_id at the ORM level. This ensures:
- No tenant can access another tenant's keys, policies, audit logs, or configuration
- Admin API endpoints require explicit
tenant_idparameters - Cross-tenant queries are architecturally impossible through the API layer
- Database-level row security policies provide defense-in-depth
Listing Tenants
curl "https://tiresias.network/v1/soulauth/admin/tenants?page=1&per_page=20" \
-H "Authorization: Bearer $ADMIN_TOKEN"
4. Policy Configuration
Policy-as-Code
Tiresias policies are defined as YAML files stored in a Git repository. This enables version control, peer review, and auditability for all access policy changes.
Directory Structure
policies/
tenants/
acme/
personas/
analyst.yaml
orchestrator.yaml
researcher.yaml
saluca/
personas/
alfred.yaml
oracle.yaml
nightwing.yaml
shared/
roles.yaml
base-constraints.yaml
Persona Policy Example
# tenants/acme/personas/analyst.yaml
persona_id: analyst-prod-01
role: analyst
description: "Production data analyst agent"
capabilities:
- read:metrics
- read:reports
- write:analysis
- execute:queries
constraints:
operating_window:
timezone: "America/New_York"
allowed_hours: "06:00-22:00"
allowed_days: ["mon", "tue", "wed", "thu", "fri"]
allowed_nodes:
- "analytics-db.internal"
- "metrics-api.internal"
session:
max_duration_minutes: 120
bind_to_ip: true
require_mfa: false
concurrency:
max_concurrent_sessions: 3
max_requests_per_minute: 100
data:
max_response_size_mb: 50
blocked_fields: ["ssn", "credit_card", "password_hash"]
escalation:
allow_temporary_grants: true
max_grant_duration_minutes: 30
require_approval: true
approvers:
- "admin@acme.com"
- "security@acme.com"
Shared Role Template
# shared/roles.yaml
roles:
analyst:
base_capabilities:
- read:metrics
- read:reports
base_constraints:
concurrency:
max_concurrent_sessions: 3
session:
max_duration_minutes: 120
orchestrator:
base_capabilities:
- read:*
- write:tasks
- execute:workflows
base_constraints:
concurrency:
max_concurrent_sessions: 10
session:
max_duration_minutes: 480
auditor:
base_capabilities:
- read:audit
- read:metrics
- read:policies
base_constraints:
data:
read_only: true
session:
max_duration_minutes: 60
Policy Resolution
When a SoulKey authenticates, the effective policy is computed by merging:
- Shared role template (from
shared/roles.yaml) - Persona-specific policy (from
tenants/<slug>/personas/<persona>.yaml) - Tenant-level overrides (from tenant settings in database)
Persona policies take precedence over role templates. Tenant overrides take precedence over both.
Git Sync Configuration
# Configure the policy repository
curl -X PUT https://tiresias.network/v1/soulauth/admin/policy-sync \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"repo_url": "https://github.com/acme/tiresias-policies.git",
"branch": "main",
"sync_interval_seconds": 60,
"deploy_key_secret": "POLICY_REPO_DEPLOY_KEY",
"path_prefix": "policies/"
}'
- Sync runs automatically at the configured interval
- Changes are validated before applying (invalid YAML is rejected)
- Sync status is available via the admin API and health endpoint
- Failed syncs generate alerts through the notification system
Policy Cache
Resolved policies are cached in the _soulauth_policy_cache table for performance:
| Column | Type | Description |
|---|---|---|
tenant_id |
UUID | Tenant identifier |
persona_id |
TEXT | Persona identifier |
resolved_policy |
JSONB | Merged and validated policy document |
policy_hash |
TEXT | SHA-256 of the resolved policy |
synced_at |
TIMESTAMPTZ | Last successful sync |
source_commit |
TEXT | Git commit hash |
# Force a policy re-sync
curl -X POST https://tiresias.network/v1/soulauth/admin/policy-sync/trigger \
-H "Authorization: Bearer $ADMIN_TOKEN"
# View sync status
curl https://tiresias.network/v1/soulauth/admin/policy-sync/status \
-H "Authorization: Bearer $ADMIN_TOKEN"
JIT Constraints
Just-In-Time constraints are evaluated at every authentication decision:
| Constraint | Description | Example |
|---|---|---|
| Operating window | Time-of-day and day-of-week restrictions | 06:00-22:00 Mon-Fri EST |
| Allowed nodes | Whitelist of backend services the agent can access | ["analytics-db", "metrics-api"] |
| Session binding | Bind session to originating IP address | bind_to_ip: true |
| Concurrency limits | Maximum simultaneous active sessions | max_concurrent_sessions: 3 |
| Request rate | Maximum requests per time window | max_requests_per_minute: 100 |
Escalation Configuration
Temporary privilege escalation for agents that need access beyond their baseline:
escalation:
allow_temporary_grants: true
max_grant_duration_minutes: 30
require_approval: true
approvers:
- "admin@acme.com"
auto_revoke_on_expiry: true
audit_all_actions: true
Requesting escalation via API:
curl -X POST https://tiresias.network/v1/soulauth/escalation/request \
-H "Authorization: Bearer $SOULKEY" \
-H "Content-Type: application/json" \
-d '{
"requested_capabilities": ["write:production-db"],
"justification": "Emergency data correction for incident INC-4521",
"duration_minutes": 15
}'
5. Detection & Response Configuration
Sigma Rules
Tiresias uses Sigma-compatible YAML rules for threat detection. Rules can be loaded from a directory or stored in the database.
Built-in detection rules:
| Rule ID | Name | Description | Default Severity |
|---|---|---|---|
CRED-001 |
Credential Stuffing | Multiple failed auth attempts from same source | Critical |
PRIV-001 |
Privilege Escalation | Capability usage outside granted scope | High |
DATA-001 |
Data Exfiltration | Abnormal data volume in responses | High |
TIME-001 |
Off-Hours Access | Authentication outside operating window | Medium |
KEY-001 |
Key Abuse | Single key used from multiple IPs simultaneously | High |
PROMPT-001 |
Prompt Injection Signal | Known injection patterns in request payload | High |
Sigma Rule Format
# rules/custom/lateral-movement.yaml
title: Lateral Movement Detection
id: custom-lat-001
status: active
level: high
description: >
Detects when an agent accesses more than 5 distinct backend
services within a 10-minute window.
detection:
condition: distinct_backends > 5
timeframe: 10m
aggregation:
field: target_service
function: count_distinct
group_by: soulkey_id
response:
actions:
- alert
- rate_limit
playbook: lateral-movement-response
Loading Custom Rules
# Upload a single rule
curl -X POST https://tiresias.network/v1/soulwatch/admin/rules \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/yaml" \
--data-binary @rules/custom/lateral-movement.yaml
# Upload all rules from a directory
for f in rules/custom/*.yaml; do
curl -X POST https://tiresias.network/v1/soulwatch/admin/rules \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/yaml" \
--data-binary @"$f"
done
# List active rules
curl https://tiresias.network/v1/soulwatch/admin/rules \
-H "Authorization: Bearer $ADMIN_TOKEN"
Playbook Configuration
Playbooks define automated response workflows triggered by detection rules:
# playbooks/credential-stuffing-response.yaml
playbook_id: cred-stuff-response
name: "Credential Stuffing Response"
description: "Automated response to credential stuffing attacks"
triggers:
- rule_id: CRED-001
min_severity: critical
actions:
- type: suspend_key
params:
reason: "Automated: credential stuffing detected"
- type: kill_sessions
params:
scope: all_active
- type: notify
params:
severity: critical
message: "Credential stuffing detected for key {soulkey_id}"
- type: quarantine
params:
policy: credential-stuffing-default
cooldown:
duration_minutes: 15
per: source_ip
Quarantine Policies
Quarantine policies are stored in the _soulauth_quarantine_policies table and define automated containment actions.
Default quarantine policies:
| Policy Name | Trigger | Actions | Duration | Release |
|---|---|---|---|---|
| Credential Stuffing | CRED-001 |
Suspend key + kill sessions | 1 hour | Auto-release |
| Scope Escalation | PRIV-001 |
Rate limit + force re-auth | 30 minutes | Auto-release |
| Rate Spike | Multiple | Suspend + kill + rate reset | Indefinite | Manual approval only |
Configure a quarantine policy:
curl -X POST https://tiresias.network/v1/soulwatch/admin/quarantine-policies \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"name": "custom-data-exfil",
"trigger_rules": ["DATA-001"],
"actions": ["suspend_key", "kill_sessions", "notify"],
"duration_minutes": 60,
"auto_release": false,
"require_approval": true,
"approvers": ["security-team@acme.com"]
}'
Release a quarantined key:
curl -X POST https://tiresias.network/v1/soulwatch/admin/quarantine/{quarantine_id}/release \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"approved_by": "admin@acme.com",
"reason": "Investigation complete - false positive confirmed"
}'
6. SIEM Integration
Supported Destinations
| Destination | Protocol | Configuration Key |
|---|---|---|
| Splunk HEC | HTTPS | splunk_hec |
| Elasticsearch / OpenSearch | HTTPS | elasticsearch |
| Azure Sentinel | HTTPS (Log Analytics API) | azure_sentinel |
| Syslog | TCP/UDP (RFC 5424) | syslog |
| Webhook | HTTPS POST | webhook |
CEF Formatting
All events are formatted using the Common Event Format (CEF) with Saluca vendor fields:
CEF:0|Saluca|Tiresias|1.0|AUTH_DENY|Authentication Denied|7|
src=10.0.1.50
suser=sk_agent_acme_analyst-01_*****
dhost=analytics-db.internal
act=deny
reason=operating_window_violation
cs1Label=tenant_id cs1=ab789b06-6624-4f92-a89b-fec960991d01
cs2Label=persona_id cs2=analyst-prod-01
cs3Label=policy_hash cs3=sha256:a1b2c3d4...
rt=2026-03-22T15:30:00Z
Configuring a SIEM Destination
Splunk HEC example:
curl -X POST https://tiresias.network/v1/soulwatch/admin/siem/destinations \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Splunk",
"type": "splunk_hec",
"config": {
"url": "https://splunk.acme.com:8088/services/collector/event",
"token": "your-hec-token",
"index": "tiresias_events",
"source": "tiresias",
"sourcetype": "tiresias:cef",
"verify_ssl": true
},
"enabled": true,
"event_filter": {
"min_severity": "medium",
"event_types": ["auth_deny", "key_suspended", "key_revoked", "quarantine_activated", "anomaly_detected"]
}
}'
Elasticsearch example:
curl -X POST https://tiresias.network/v1/soulwatch/admin/siem/destinations \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Elastic SIEM",
"type": "elasticsearch",
"config": {
"url": "https://elastic.acme.com:9200",
"index_pattern": "tiresias-events-%Y.%m.%d",
"username": "tiresias_writer",
"password": "your-password",
"verify_ssl": true
},
"enabled": true
}'
Syslog (RFC 5424) example:
curl -X POST https://tiresias.network/v1/soulwatch/admin/siem/destinations \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Syslog Collector",
"type": "syslog",
"config": {
"host": "syslog.acme.com",
"port": 514,
"protocol": "tcp",
"facility": "auth",
"app_name": "tiresias"
},
"enabled": true
}'
Event Forwarder Behavior
The SIEM event forwarder uses a buffered, batched architecture:
| Parameter | Value | Description |
|---|---|---|
| Buffer size | 100 events | Events are buffered in memory before flush |
| Flush interval | 30 seconds | Maximum time between flushes |
| Dead-letter queue | 10,000 events max | Failed events are queued for retry |
| Retry strategy | Exponential backoff | 1s, 2s, 4s, 8s, 16s (max 5 retries) |
| Batch size | 50 events | Events per HTTP request |
If the dead-letter queue reaches capacity, oldest events are dropped and a critical alert is generated.
# View SIEM destination health
curl https://tiresias.network/v1/soulwatch/admin/siem/destinations/{destination_id}/health \
-H "Authorization: Bearer $ADMIN_TOKEN"
# View dead-letter queue stats
curl https://tiresias.network/v1/soulwatch/admin/siem/dlq/stats \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Retry dead-letter queue
curl -X POST https://tiresias.network/v1/soulwatch/admin/siem/dlq/retry \
-H "Authorization: Bearer $ADMIN_TOKEN"
7. Notification Routing
Supported Notification Sinks
| Sink | Type | Configuration Required |
|---|---|---|
| PagerDuty | Incident management | Integration key (Events API v2) |
| Slack | Chat | Webhook URL or Bot token + channel |
| Microsoft Teams | Chat | Incoming webhook URL |
| OpsGenie | Incident management | API key + team |
| Email (SMTP) | SMTP host, port, credentials | |
| AWS SNS | Cloud messaging | Topic ARN, region, credentials |
| Telegram | Chat | Bot token + chat ID |
| Webhook | Generic HTTP | URL + optional headers |
Default Severity Routing
| Severity | Default Destinations | Response Time Target |
|---|---|---|
| Critical | PagerDuty + Slack + Email | Immediate (< 5 min) |
| High | Slack + Email | < 30 min |
| Medium | Slack | < 4 hours |
| Low | Log only | Best effort |
Configuring Notification Sinks
Slack example:
curl -X POST https://tiresias.network/v1/soulwatch/admin/notifications/sinks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Security Slack Channel",
"type": "slack",
"config": {
"webhook_url": "https://hooks.slack.com/services/T00/B00/xxx",
"channel": "#tiresias-alerts",
"username": "Tiresias",
"icon_emoji": ":shield:"
},
"severity_filter": ["critical", "high", "medium"],
"enabled": true
}'
PagerDuty example:
curl -X POST https://tiresias.network/v1/soulwatch/admin/notifications/sinks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "PagerDuty On-Call",
"type": "pagerduty",
"config": {
"integration_key": "your-events-api-v2-key",
"severity_mapping": {
"critical": "critical",
"high": "error",
"medium": "warning",
"low": "info"
}
},
"severity_filter": ["critical"],
"enabled": true
}'
Per-Tenant Overrides
Override default routing for specific tenants:
curl -X PUT https://tiresias.network/v1/soulwatch/admin/notifications/routing/{tenant_id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"overrides": {
"critical": ["pagerduty", "slack", "email", "telegram"],
"high": ["slack", "email"],
"medium": ["slack"],
"low": ["log"]
}
}'
Circuit Breaker
The notification system includes a circuit breaker to prevent cascading failures:
| Parameter | Value | Description |
|---|---|---|
| Failure threshold | 3 consecutive failures | Trips the circuit breaker |
| Recovery timeout | 5 minutes | Duration the sink is skipped |
| Half-open test | 1 request | Single test request after recovery timeout |
| Reset on success | Immediate | Counter resets after successful delivery |
When a circuit breaker trips, events are routed to remaining healthy sinks and a warning is logged.
Rate Limiting
| Parameter | Value | Description |
|---|---|---|
| Rate limit | 10 alerts / 60 seconds | Per sink rate limit |
| Burst allowance | 5 additional | Brief burst above steady-state |
| Dedup window | 5 minutes | Identical alerts are deduplicated |
Alerts that exceed the rate limit are queued and delivered when capacity is available, or dropped if the queue is full (with a summary alert sent).
8. SoulGate Configuration
SoulGate is the API gateway that sits in front of backend services, enforcing authentication, rate limiting, and content inspection.
Upstream Registry
Register backend services that SoulGate proxies:
curl -X POST https://tiresias.network/v1/soulgate/admin/upstreams \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "analytics-api",
"url": "https://analytics-api.internal:8080",
"health_check": {
"path": "/health",
"interval_seconds": 10,
"timeout_seconds": 5,
"unhealthy_threshold": 3
},
"timeout": {
"connect_seconds": 5,
"read_seconds": 30,
"write_seconds": 30
},
"retry": {
"max_retries": 2,
"retry_on": ["502", "503", "504"]
}
}'
Rate Limit Policies
Rate limits can be applied at multiple granularities using sliding window counters:
curl -X POST https://tiresias.network/v1/soulgate/admin/rate-limits \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "standard-agent-limits",
"rules": [
{
"scope": "per_soulkey",
"limit": 100,
"window_seconds": 60,
"action": "reject",
"response_code": 429
},
{
"scope": "per_tenant",
"limit": 1000,
"window_seconds": 60,
"action": "reject",
"response_code": 429
},
{
"scope": "per_endpoint",
"match": "/v1/ai/completions",
"limit": 20,
"window_seconds": 60,
"action": "queue",
"queue_timeout_seconds": 30
}
]
}'
| Scope | Description |
|---|---|
per_soulkey |
Limits applied to each individual SoulKey |
per_tenant |
Aggregate limit for all keys in a tenant |
per_endpoint |
Limits on specific API paths |
global |
System-wide limit across all tenants |
Circuit Breaker Settings
SoulGate includes an intelligent circuit breaker with anti-weaponization protections:
| Parameter | Default | Description |
|---|---|---|
failure_threshold |
5 | Consecutive failures to trip the breaker |
cooldown_seconds |
30 | Duration in open state before half-open test |
min_requests |
20 | Minimum requests before breaker can activate |
per_source_ratio |
true | Track failure ratio per source, not globally |
Anti-weaponization: The min_requests threshold (default: 20) prevents a malicious agent from deliberately sending a small number of failing requests to trip the circuit breaker and deny service to legitimate users. The per_source_ratio check ensures one agent's failures do not affect another's access.
curl -X PUT https://tiresias.network/v1/soulgate/admin/circuit-breaker \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"failure_threshold": 5,
"cooldown_seconds": 30,
"min_requests": 20,
"per_source_ratio": true,
"exclude_status_codes": [400, 401, 403, 404]
}'
Prompt Injection Detection
SoulGate inspects request payloads for prompt injection patterns:
| Parameter | Default | Description |
|---|---|---|
| Pattern count | 40+ | Built-in regex and semantic patterns |
| Warn threshold | 0.3 | Risk score that generates a warning |
| Block threshold | 0.7 | Risk score that blocks the request |
| Custom patterns | Supported | Upload additional patterns via API |
# Update prompt injection thresholds
curl -X PUT https://tiresias.network/v1/soulgate/admin/prompt-injection \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"warn_threshold": 0.3,
"block_threshold": 0.7,
"custom_patterns": [
{
"name": "internal-system-prompt-leak",
"pattern": "(?i)(show|reveal|print|output)\\s+(your|the)\\s+(system|initial)\\s+prompt",
"weight": 0.5
}
],
"exempt_endpoints": ["/v1/admin/*"],
"log_blocked_payloads": true
}'
IP/Geo Access Control
Configure IP allowlists and denylists per tenant:
curl -X PUT https://tiresias.network/v1/soulgate/admin/access-control/{tenant_id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ip_allowlist": [
"10.0.0.0/8",
"172.16.0.0/12",
"203.0.113.50/32"
],
"ip_denylist": [],
"geo_allowlist": ["US", "CA", "GB", "DE"],
"geo_denylist": [],
"mode": "allowlist",
"enforce": true
}'
| Mode | Behavior |
|---|---|
allowlist |
Only listed IPs/geos are permitted; all others denied |
denylist |
Only listed IPs/geos are blocked; all others permitted |
disabled |
No IP/geo restrictions (default) |
Gateway API Key Management
SoulGate can issue its own API keys for external consumers (distinct from SoulKeys):
# Issue a gateway key
curl -X POST https://tiresias.network/v1/soulgate/admin/api-keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "partner-integration",
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"scopes": ["read:public-api"],
"rate_limit_override": {
"limit": 500,
"window_seconds": 60
},
"expires_at": "2027-03-22T00:00:00Z"
}'
# Rotate a gateway key
curl -X POST https://tiresias.network/v1/soulgate/admin/api-keys/{key_id}/rotate \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Revoke a gateway key
curl -X DELETE https://tiresias.network/v1/soulgate/admin/api-keys/{key_id} \
-H "Authorization: Bearer $ADMIN_TOKEN"
9. Monitoring & Observability
Health Endpoint
All services expose a /health endpoint:
# Basic health check
curl https://tiresias.network/health
# Detailed component breakdown
curl "https://tiresias.network/health?detail=true"
Detailed response:
{
"status": "healthy",
"version": "1.0.0",
"uptime_seconds": 86400,
"components": {
"database": {
"status": "healthy",
"latency_ms": 2.3,
"pool_size": 10,
"active_connections": 3
},
"jwt_keys": {
"status": "healthy",
"kid": "kid-2026-03",
"algorithm": "ES256",
"public_key_loaded": true,
"private_key_loaded": true
},
"policy_sync": {
"status": "healthy",
"last_sync": "2026-03-22T14:55:00Z",
"source_commit": "a1b2c3d4",
"cached_policies": 47
},
"detection_engine": {
"status": "healthy",
"active_rules": 12,
"events_processed_1h": 15420
}
}
}
Component statuses:
| Status | Meaning |
|---|---|
healthy |
Component is operating normally |
degraded |
Component is functional but experiencing issues |
unhealthy |
Component has failed; service may be impaired |
Prometheus Metrics
The /metrics endpoint exposes 20+ metrics in Prometheus format:
# Scrape metrics (with optional auth)
curl https://tiresias.network/metrics \
-H "Authorization: Bearer $METRICS_AUTH_TOKEN"
Prometheus scrape configuration:
# prometheus.yml
scrape_configs:
- job_name: "soulauth"
scheme: https
bearer_token: "your-metrics-auth-token"
static_configs:
- targets: ["tiresias.network:443"]
metrics_path: /metrics
scrape_interval: 15s
Key Metrics Reference
Authentication Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
soulauth_auth_requests_total |
Counter | decision, tenant_id |
Total auth decisions (grant/deny) |
soulauth_auth_latency_seconds |
Histogram | decision |
Auth decision latency |
soulauth_active_sessions |
Gauge | tenant_id |
Currently active sessions |
soulauth_token_issued_total |
Counter | tenant_id, type |
Tokens issued |
soulauth_token_refreshed_total |
Counter | tenant_id |
Token refresh operations |
SoulKey Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
soulauth_soulkeys_active |
Gauge | tenant_id |
Active SoulKeys |
soulauth_soulkey_operations_total |
Counter | operation |
Key lifecycle operations |
Policy Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
soulauth_policy_syncs_total |
Counter | status |
Policy sync attempts (success/failure) |
soulauth_policy_sync_latency_seconds |
Histogram | -- | Sync duration |
soulauth_cached_policies |
Gauge | -- | Policies in cache |
Rate Limiting Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
soulauth_rate_limit_hits_total |
Counter | tenant_id, scope |
Rate limit rejections |
soulauth_rate_limit_remaining |
Gauge | tenant_id, scope |
Remaining quota |
Detection Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
soulauth_anomalies_total |
Counter | rule_id, severity |
Anomalies detected |
soulauth_quarantine_active |
Gauge | tenant_id |
Active quarantines |
soulauth_detection_latency_seconds |
Histogram | -- | Detection pipeline latency |
System Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
soulauth_health_check_status |
Gauge | component |
Component health (1=healthy, 0=unhealthy) |
soulauth_db_pool_active |
Gauge | -- | Active database connections |
soulauth_db_pool_overflow |
Gauge | -- | Overflow connections in use |
soulauth_siem_events_forwarded_total |
Counter | destination |
Events sent to SIEM |
soulauth_siem_dlq_size |
Gauge | destination |
Dead-letter queue depth |
soulauth_notifications_sent_total |
Counter | sink, severity |
Notifications dispatched |
Recommended Alert Rules
# Prometheus alerting rules
groups:
- name: tiresias-critical
rules:
- alert: HighAuthDenyRate
expr: |
rate(soulauth_auth_requests_total{decision="deny"}[5m])
/ rate(soulauth_auth_requests_total[5m]) > 0.2
for: 5m
labels:
severity: critical
annotations:
summary: "Auth deny rate exceeds 20% over 5 minutes"
- alert: AnomalySpike
expr: rate(soulauth_anomalies_total[5m]) > 10
for: 2m
labels:
severity: critical
annotations:
summary: "Anomaly detection rate exceeds 10/min"
- alert: ServiceUnhealthy
expr: soulauth_health_check_status == 0
for: 1m
labels:
severity: critical
annotations:
summary: "{{ $labels.component }} health check failing"
- alert: DatabasePoolExhaustion
expr: soulauth_db_pool_active / soulauth_db_pool_active + soulauth_db_pool_overflow > 0.9
for: 5m
labels:
severity: high
annotations:
summary: "Database connection pool >90% utilized"
- alert: SIEMDeadLetterQueueGrowing
expr: soulauth_siem_dlq_size > 5000
for: 10m
labels:
severity: high
annotations:
summary: "SIEM dead-letter queue exceeds 5000 events"
- alert: PolicySyncFailure
expr: soulauth_policy_syncs_total{status="failure"} > 0
for: 15m
labels:
severity: high
annotations:
summary: "Policy sync has been failing for 15+ minutes"
10. Audit & Compliance
Hash-Chained Audit Log
Every administrative action and authentication decision is recorded in a tamper-evident audit log. Each entry is linked to the previous entry via a SHA-256 hash chain.
Hash chain structure:
entry_hash = SHA-256(
previous_hash +
timestamp +
event_type +
actor +
tenant_id +
payload_json
)
If any historical entry is modified, the chain breaks and all subsequent hashes become invalid. Chain integrity is verified automatically during audit queries.
Event Types
| Event Type | Category | Description |
|---|---|---|
key_issued |
Key Management | New SoulKey created |
key_suspended |
Key Management | SoulKey suspended |
key_reinstated |
Key Management | SoulKey reinstated from suspension |
key_revoked |
Key Management | SoulKey permanently revoked |
key_expired |
Key Management | SoulKey auto-expired |
auth_grant |
Authentication | Authentication request approved |
auth_deny |
Authentication | Authentication request denied |
token_issued |
Token Operations | JWT token issued |
token_refreshed |
Token Operations | JWT token refreshed |
capability_issued |
Authorization | Capability grant issued |
capability_escalated |
Authorization | Temporary privilege escalation |
policy_synced |
Policy | Policy sync completed |
policy_failed |
Policy | Policy sync failed |
quarantine_activated |
Detection | Quarantine policy triggered |
quarantine_released |
Detection | Quarantine released |
anomaly_detected |
Detection | Anomaly rule matched |
playbook_executed |
Detection | Response playbook triggered |
tenant_created |
Administration | New tenant created |
tenant_updated |
Administration | Tenant settings changed |
siem_forwarded |
Integration | Event forwarded to SIEM |
config_changed |
Administration | System configuration modified |
Querying the Audit Log
# Query by tenant
curl "https://tiresias.network/v1/soulauth/admin/audit?tenant_id={tenant_id}&page=1&per_page=50" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Query by event type
curl "https://tiresias.network/v1/soulauth/admin/audit?event_type=auth_deny&since=24h" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Query by SoulKey
curl "https://tiresias.network/v1/soulauth/admin/audit?soulkey_id={soulkey_id}&since=7d" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Query by date range
curl "https://tiresias.network/v1/soulauth/admin/audit?from=2026-03-01T00:00:00Z&to=2026-03-22T23:59:59Z" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Verify chain integrity
curl -X POST "https://tiresias.network/v1/soulauth/admin/audit/verify-integrity" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"from": "2026-03-01T00:00:00Z",
"to": "2026-03-22T23:59:59Z"
}'
Integrity verification response:
{
"status": "valid",
"entries_checked": 15420,
"chain_start": "2026-03-01T00:00:01Z",
"chain_end": "2026-03-22T23:58:30Z",
"first_hash": "sha256:a1b2c3...",
"last_hash": "sha256:d4e5f6...",
"breaks_found": 0
}
Retention Policies
Audit log retention is configured per tier:
| Tier | Retention Period | Archival |
|---|---|---|
| Community | 1 day | None |
| Starter | 7 days | Optional export |
| Pro | 30 days | Automatic cold storage |
| Enterprise | 90 days | Automatic cold storage + compliance hold |
Configure custom retention:
curl -X PUT https://tiresias.network/v1/soulauth/admin/audit/retention/{tenant_id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"retention_days": 90,
"archive_to": "gcs://tiresias-audit-archive/acme/",
"compliance_hold": true
}'
Compliance Reports
Generate compliance-ready reports for standard frameworks:
# SOC 2 Type II report
curl -X POST "https://tiresias.network/v1/soulauth/admin/compliance/reports" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"framework": "soc2",
"tenant_id": "ab789b06-6624-4f92-a89b-fec960991d01",
"period": {
"from": "2026-01-01T00:00:00Z",
"to": "2026-03-22T23:59:59Z"
},
"format": "pdf"
}'
Supported frameworks:
| Framework | Report Contents |
|---|---|
| SOC 2 | Access controls, key management, audit trail integrity, incident response |
| ISO 27001 | Information security controls, risk assessment, policy compliance |
| NIST 800-53 | Security and privacy controls mapping, continuous monitoring evidence |
Reports include: audit chain integrity verification, access pattern analysis, policy change history, incident response timelines, and key lifecycle documentation.
11. Troubleshooting
Connection Pool Exhaustion
Symptoms:
- Increasing response latency
TimeoutErrororQueuePool limitin logssoulauth_db_pool_overflowmetric at maximum
Resolution:
Check current pool usage:
curl "https://tiresias.network/health?detail=true" | jq '.components.database'Increase pool size (restart required):
# In .env or Kubernetes secret SOULAUTH_DB_POOL_SIZE=20 SOULAUTH_DB_MAX_OVERFLOW=40If using Cloud SQL, increase
max_connections:gcloud sql instances patch tiresias-db \ --database-flags max_connections=200Investigate connection leaks: check for long-running queries in PostgreSQL:
SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes' AND state != 'idle' ORDER BY duration DESC;
Anomaly Detection Baseline Cold Start
Symptoms:
- High false-positive rate for new agents
- Anomaly alerts for normal behavior patterns
Explanation: The anomaly detection engine requires approximately 7 days of baseline data per agent to establish normal behavior patterns. During this period, detection sensitivity is intentionally reduced.
Resolution:
- This is expected behavior. No action required.
- Optionally suppress alerts for new agents during the baseline period:
curl -X PUT https://tiresias.network/v1/soulwatch/admin/detection/baseline-config \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "cold_start_days": 7, "cold_start_severity_floor": "high", "notify_on_baseline_complete": true }'
Policy Sync Failures
Symptoms:
soulauth_policy_syncs_total{status="failure"}incrementing- Stale
synced_attimestamps in_soulauth_policy_cache - Policy changes not taking effect
Diagnostic steps:
Check sync status:
curl https://tiresias.network/v1/soulauth/admin/policy-sync/status \ -H "Authorization: Bearer $ADMIN_TOKEN"Verify Git repository access:
# From within the container or pod git ls-remote https://github.com/your-org/tiresias-policies.gitValidate YAML syntax:
# Install yamllint if not present pip install yamllint yamllint policies/Check the
_soulauth_policy_cachetable:SELECT tenant_id, persona_id, synced_at, source_commit, LENGTH(resolved_policy::text) AS policy_size FROM _soulauth_policy_cache ORDER BY synced_at DESC LIMIT 20;Force a manual re-sync:
curl -X POST https://tiresias.network/v1/soulauth/admin/policy-sync/trigger \ -H "Authorization: Bearer $ADMIN_TOKEN"
Token Validation Errors
Symptoms:
401 Unauthorizedresponses with valid SoulKeysinvalid_tokenortoken_expirederror codes- JWT verification failures in logs
Diagnostic steps:
Verify the ES256 key pair: confirm the private and public keys are a matching pair:
# Extract public key from private key and compare openssl ec -in keys/private.pem -pubout 2>/dev/null | diff - keys/public.pemCheck clock skew: JWT validation is sensitive to time differences between services:
# On each service host/container date -u # Ensure all clocks are within 30 seconds of each other # Use NTP if not already configuredVerify JWT_KID matches: the key ID in the token must match the server's current KID:
# Decode a token header (without validation) echo "$TOKEN" | cut -d. -f1 | base64 -d 2>/dev/null | jq . # Should show: {"alg":"ES256","kid":"kid-2026-03","typ":"JWT"} # The "kid" must match SOULAUTH_JWT_KIDCheck key file permissions: key files must be readable by the service user:
ls -la keys/private.pem keys/public.pem # Should be readable by the service process (e.g., 0400 or 0440)
Common Error Codes
| HTTP Code | Error | Cause | Resolution |
|---|---|---|---|
| 401 | invalid_soulkey |
SoulKey not found or revoked | Verify key status, re-issue if needed |
| 401 | expired_token |
JWT has expired | Client should refresh the token |
| 401 | invalid_signature |
JWT signature verification failed | Check ES256 key pair and KID |
| 403 | insufficient_scope |
Action not in granted capabilities | Update persona policy |
| 403 | operating_window |
Request outside allowed hours | Adjust policy or wait |
| 403 | quarantined |
Key is under quarantine | Check detection alerts, release if appropriate |
| 429 | rate_limited |
Rate limit exceeded | Back off and retry, or request limit increase |
| 503 | circuit_open |
Circuit breaker tripped for upstream | Wait for cooldown, check upstream health |
Getting Support
- Documentation: https://docs.tiresias.network
- Community: GitHub Discussions
- Starter/Pro: Email support with 24-hour response SLA
- Enterprise: Dedicated support channel with 4-hour response SLA
This document is maintained by the Tiresias platform team. For corrections or additions, open a pull request against the documentation repository.