# cortex.example.toml — example configuration
#
# Copy to cortex.toml and adjust for your environment.
#
# Environment variable overrides use CORTEX_ prefix with __ separators:
#   CORTEX_GATEWAY__LISTEN=0.0.0.0:31313

# Path to the model catalogue (limits, cost, pinning, aliases, feasibility).
# Defaults to the packaged location below; uncomment to override for a
# non-packaged / local run.
# models_config = "/etc/cortex/models.toml"

[gateway]
listen = "0.0.0.0:31313"
metrics_listen = "0.0.0.0:31314"

[eviction]
strategy = "lru"
# Restart neurons after this many load/unload cycles to defragment VRAM.
# Set to 0 to disable.
defrag_after_cycles = 50

# -- Nodes ---------------------------------------------------------------
# Each [[nodes]] entry declares a neuron daemon in the fleet.
# Models are discovered by polling the neuron's /models endpoint.
# Pinned models (see models.toml) are never evicted.

[[nodes]]
name = "gpu-large"
endpoint = "http://gpu-large.internal:8080"
vram_mb = 49152           # e.g. 2x RTX 4090 (48 GB combined)
pinned = [
    "your-org/large-model",
]

[[nodes]]
name = "gpu-medium"
endpoint = "http://gpu-medium.internal:8080"
vram_mb = 24576           # e.g. RTX 4090 (24 GB)
pinned = [
    "your-org/medium-model",
]

[[nodes]]
name = "gpu-small"
endpoint = "http://gpu-small.internal:8080"
vram_mb = 12288           # e.g. RTX 3060 (12 GB)
pinned = [
    "your-org/embedding-model",
]

# -- Entitlements (multi-tenant governance, #47) -------------------------
# Identity + per-key token budgets. Omit this section entirely for the
# legacy single-operator behaviour: requests are anonymous and uncapped.
#
# The local/static provider below is the source of truth for accounts,
# keys, and hard caps until the upstream clearing house exists. Identity
# rides standard bearer auth only — clients send
#   Authorization: Bearer <key>
# no custom headers or body fields.

[entitlements]
# Reject unauthenticated requests with 401 invalid_api_key. Leave false
# (allow-anonymous) during rollout; flip to true once keys are issued.
require_auth = false

# One entry per API key.
[[entitlements.keys]]
key = "sk-example-rolling"        # the bearer token the client sends
account_id = "team-research"      # billable account (keys may share one)
key_id = "research-ci"            # stable label for ledger/metrics (optional)
hard_cap = 5_000_000              # hard token cap over the window
# Rolling window that resets — over-cap requests get 429 rate_limit_exceeded
# + Retry-After, so well-behaved clients (opencode/AI SDK) back off and retry.
window = { kind = "rolling", seconds = 3600 }

[[entitlements.keys]]
key = "sk-example-balance"
account_id = "team-research"
key_id = "research-prepaid"
hard_cap = 20_000_000
# Hard balance, no reset — exhaustion returns 429 insufficient_quota
# (the client surfaces and stops). This is the default when `window` is
# omitted. Never 402.
window = { kind = "balance" }

[[entitlements.keys]]
key = "sk-example-infra"
account_id = "operator"
key_id = "infra"
# No hard_cap → uncapped operator infra key (own fleet, own use). Still
# metered for visibility.
