# Install from PyPI
pip install zelos-runtime
# Create config and run
cp zelos.yaml.example zelos.yaml # edit config to your needs
python3 start.py # start Runtime + Dashboard
Or from source:
git clone https://github.com/AI-Zelos/zelos.git && cd zelos
pip install -e ".[dev]"
make dev
make build # zelos:0.8.1
make run # docker compose up -d
make stop # docker compose down
# With Redis for persistent storage:
make run-storage # includes redis profile
kubectl apply -f deploy/k8s/deployment.yaml
kubectl apply -f deploy/k8s/service.yaml
| Endpoint | Probe Type | Port | Response |
|---|---|---|---|
GET /live |
Liveness | 9876 | {"status": "alive"} |
GET /ready |
Readiness | 9876 | {"status": "ready"/"not_ready"} |
Example K8s config:
livenessProbe:
httpGet:
path: /live
port: 9876
initialDelaySeconds: 5
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 9876
initialDelaySeconds: 3
periodSeconds: 10
Scrape http://<runtime>:9876/metrics for:
| Metric | Type | Description |
|---|---|---|
zelos_goals_active |
Gauge | Currently active goals |
zelos_goals_completed_total |
Counter | Total completed goals |
zelos_tasks_completed_total |
Counter | Total completed tasks |
zelos_tasks_failed_total |
Counter | Total failed tasks |
zelos_agents_connected |
Gauge | Currently connected agents |
zelos_agents_disconnected |
Gauge | Disconnected agents |
Import deploy/grafana/zelos-dashboard.json into Grafana.
Requires Prometheus datasource configured.
from zelos.security import APIKeyManager
mgr = APIKeyManager(
max_failures=10, # auto-revoke threshold
failure_window_seconds=60.0, # sliding window
auto_revoke=True, # enable brute-force protection
)
key = mgr.generate_key("admin", "production-admin-key")
# → "zelos_<128 hex chars>" — save this, it's shown only once
mgr.validate(key) # → {"role": "admin", ...}
mgr.revoke(key) # → True
max_failures within failure_window_secondslogger.export_json_file("/var/log/zelos/audit.json")
from zelos.coordination import EtcdCoordinationBackend, CoordinationNode
backend = EtcdCoordinationBackend({"endpoints": "localhost:2379", "prefix": "/zelos/"})
backend.connect()
backend.register_node(CoordinationNode(node_id="node-1", host="10.0.0.1", port=9001))
backend.elect_leader("node-1", ttl_seconds=30)
leader = backend.get_leader() # → "node-1"
backend.heartbeat("node-1") # renew lease
from zelos.messaging_nats import NatsMessageBus
bus = NatsMessageBus({"servers": ["nats://localhost:4222"]})
bus.connect()
bus.subscribe("zelos.events", lambda data, headers: process(data))
bus.publish("zelos.events", {"event_type": "task.completed"})
go get github.com/zelos/zelos-go
c := client.New("http://localhost:9876", "zk-client-dev")
health, _ := c.Health()
goal, _ := c.SubmitGoal("Build a landing page", "high")
┌──────────────────────────────────────────────┐
│ Node 1 (Leader) │
│ ZelosRuntime + etcd + NATS │
│ IP: 10.0.0.1:9876 │
└──────────┬──────────┬────────────────────────┘
│ │
┌─────▼───┐ ┌──▼──────┐
│ Node 2 │ │ Node 3 │
│ 10.0.0.2│ │ 10.0.0.3│
└─────────┘ └─────────┘
| Component | Purpose | Default Port |
|---|---|---|
| etcd | Leader election + node discovery | 2379 |
| NATS | Cross-node EventBus + messaging | 4222 |
| Zelos Runtime | Agent orchestration | 9876 |
# etcd (all nodes connect to same cluster)
docker run -d --name etcd -p 2379:2379 \
bitnami/etcd:latest
# NATS
docker run -d --name nats -p 4222:4222 \
nats:latest
# zelos-node-1.yaml
runtime:
instance_id: "zelos-node-1"
api:
host: "10.0.0.1"
port: 9876
distributed:
enabled: true
node_id: "node-1"
peers: ["10.0.0.1:9877", "10.0.0.2:9877", "10.0.0.3:9877"]
storage:
type: postgresql
url: "postgresql://postgres:zelos@10.0.0.1:5432/zelos"
coordination:
type: etcd
endpoints: "10.0.0.1:2379"
messaging:
type: nats
servers: ["nats://10.0.0.1:4222"]
# Node 1 (expects to become leader)
python3 start.py --config zelos-node-1.yaml &
# Node 2
python3 start.py --config zelos-node-2.yaml &
# Node 3
python3 start.py --config zelos-node-3.yaml &
curl http://10.0.0.1:9876/api/v1/cluster
# → {"is_leader": true, "peers": ["node-1", "node-2", "node-3"], "healthy": 3}
curl http://10.0.0.2:9876/api/v1/cluster
# → {"is_leader": false, "leader": "node-1"}
1. All nodes register in etcd: /zelos/nodes/{node_id}
2. Smallest node_id wins (Bully algorithm)
3. Leader holds etcd lease, renews via heartbeat
4. If leader lease expires → new election triggered
5. Watchers (NATS subscribers) notified of leader change
Node-2 idle (queue depth=0) → queries etcd for busiest node → steals READY tasks
Node-3 overloaded (queue depth=15) → Node-1 and Node-2 steal work
| Failure | Behavior |
|---|---|
| Leader crash | etcd lease expires → Node-2/Node-3 elect new leader (<5s) |
| Worker crash | In-flight tasks re-assigned via NATS |
| etcd down | Falls back to InMemory coordination (single-node mode) |
| NATS down | Falls back to InMemory message bus |
| Symptom | Check |
|---|---|
| Runtime won't start | python3 -c "from zelos.runtime import ZelosRuntime; ZelosRuntime().start()" |
| Agent won't register | Verify API key role >= agent |
| Tasks stuck in READY | Check capability name matches agent registration |
| High failure rate | Check anomaly detection — keys may be auto-revoked |
/ready returns 503 |
Check GET /api/v1/health for component status |
GET /api/v1/health → {
"status": "healthy",
"components": {
"kernel": "healthy",
"plugins": {"total": N, "healthy": N},
"agents": {"connected": N, "disconnected": N},
"security": {"audit_events": N},
"cluster": {"enabled": bool, "is_leader": bool}
}
}
Enable PersistentEventStore with a storage backend to survive restarts:
from zelos.event_bus import PersistentEventStore
from zelos.storage import PostgreSQLStorageBackend
storage = PostgreSQLStorageBackend({"url": "postgresql://..."})
storage.connect()
store = PersistentEventStore(storage)
# After crash:
recovered = store.recover() # replays all persisted events
Save Goal/Agent state before shutdown:
storage.set_state(f"goal-{goal_id}", goal_state)
# After restart:
saved = storage.get_state(f"goal-{goal_id}")
See zelos.yaml for all options. Key sections:
| Section | Purpose |
|---|---|
runtime.api |
Host, port, auth keys |
runtime.limits |
max_goals, max_tasks_per_goal |
security |
audit_max_events, TLS/mTLS paths |
multi_tenancy |
enabled, tenant configs |
distributed |
enabled, node_id, peers |
plugins |
All plugin declarations (storage, memory, policy, verifier, planner, adapter) |