Configuration¶
Every parameter shown in previous sections — mailbox capacity, supervision strategy, gossip interval, failure detector threshold — can be set programmatically. But when deploying the same codebase across environments (dev, staging, production), hardcoding these values becomes impractical. Casty supports TOML-based configuration via a casty.toml file.
Configuration is always optional. When absent, all parameters use the same defaults as the programmatic API. When present, the TOML file produces the existing dataclasses — ClusterConfig, ReplicationConfig, etc. — through a pure loader function. No global state, no singletons, no magic.
File Structure¶
A complete casty.toml covering all configurable aspects:
[system]
name = "my-app"
# --- Global defaults (apply to all actors) ---
[defaults.mailbox]
capacity = 1000
strategy = "drop_new" # drop_new | drop_oldest | reject
[defaults.supervision]
strategy = "restart" # restart | stop | escalate
max_restarts = 3
within_seconds = 60.0
[defaults.sharding]
num_shards = 256
[defaults.replication]
replicas = 2
min_acks = 1
ack_timeout = 5.0
# --- Cluster ---
[cluster]
host = "0.0.0.0"
port = 25520
seed_nodes = ["node1:25520", "node2:25520"]
roles = ["worker"]
[cluster.gossip]
interval = 1.0
[cluster.heartbeat]
interval = 0.5
availability_check_interval = 2.0
[cluster.tls]
certfile = "certs/node.pem"
cafile = "certs/ca.pem"
# keyfile = "certs/node.key" # optional, if key is separate from certfile
[cluster.failure_detector]
threshold = 8.0
max_sample_size = 200
min_std_deviation_ms = 100.0
acceptable_heartbeat_pause_ms = 0.0
first_heartbeat_estimate_ms = 1000.0
# --- Per-actor overrides ---
[actors.orders]
sharding = { num_shards = 512 }
replication = { replicas = 3, min_acks = 2 }
[actors.my-worker]
mailbox = { capacity = 5000, strategy = "reject" }
supervision = { strategy = "stop" }
[actors."child-\\d+"]
mailbox = { capacity = 100, strategy = "drop_oldest" }
Every key is optional. A minimal casty.toml can be just:
Loading Configuration¶
load_config() accepts an explicit path or discovers casty.toml automatically by walking up from the current working directory (like pyproject.toml):
# Auto-discovery — walks up from CWD looking for casty.toml
config = load_config()
# Explicit path
config = load_config(Path("infra/casty.toml"))
For a local ActorSystem, pass the config to the constructor:
config = load_config()
async with ActorSystem(config=config) as system:
ref = system.spawn(my_behavior(), "my-actor")
# mailbox and supervision come from casty.toml
For a ClusteredActorSystem, use from_config to derive host, port, seed nodes, and cluster tuning from the [cluster] section:
config = load_config()
async with ClusteredActorSystem.from_config(config) as system:
# host, port, seed_nodes, roles, TLS, gossip interval,
# heartbeat interval, failure detector — all from casty.toml
...
Programmatic overrides take precedence over the file — useful for dynamic ports in tests or container orchestration:
async with ClusteredActorSystem.from_config(
config,
host="10.0.0.5",
port=0, # OS-assigned port
) as system:
...
Per-Actor Overrides¶
Actor keys under [actors.*] are matched against the actor name at spawn() time using re.fullmatch(). Simple names like orders match exactly. Regex patterns use quoted TOML keys:
# Exact match — only the actor named "orders"
[actors.orders]
mailbox = { capacity = 5000 }
# Regex — matches "sensor-001", "sensor-042", etc.
[actors."sensor-\\d+"]
mailbox = { capacity = 100, strategy = "drop_oldest" }
Resolution order: per-actor override > [defaults.*] > dataclass defaults. First matching pattern wins (definition order in TOML).
[defaults.mailbox]
capacity = 1000
strategy = "drop_new"
[actors.orders]
mailbox = { capacity = 5000 }
# strategy inherits "drop_new" from [defaults.mailbox]
Internal actors (names starting with _) are never resolved against config — they always use framework defaults.