Architecture
SeyalRun is a microservices platform. Each service owns its own tables and exposes GET /health and GET /metrics; nothing but edge-proxy is published to the host.
Services
| Service | Role |
|---|---|
edge-proxy | TLS termination, HTTP → HTTPS redirect. The only service published to the host, on EDGE_HTTP_PORT / EDGE_HTTPS_PORT. |
api-gateway | Public API surface. Session handling, CORS (FRONTEND_ORIGIN), per-IP/user rate limiting via Redis. |
identity-service | Users, auth, sessions, RBAC, Zabbix SSO exchange. Internal-only. |
inventory-service | Hosts, host groups, and the AES-256-GCM credential vault. Internal-only. |
terminal-service | Browser-based SSH terminal sessions. |
recording-service | Session recording storage and replay. |
automation-service | Scripted job execution across managed hosts, Zabbix-trigger bindings. |
metrics-service | Aggregated platform metrics. |
zabbix-integration-service | Serves the agentless self-monitoring webhook (/webhook/zabbix/monitor) and Zabbix event integration. |
frontend | The Vue 3 single-page app served to the browser. |
Trust model
In plain terms: your browser only ever talks to one address. Everything behind it stays private, and nothing runs without proving who it is first.
- Only
edge-proxyis public. It's the one address your browser ever connects to. api-gatewayis the front door. Every request from the browser passes through it — this is also where rate limiting and CORS are enforced.- Everything past that is locked.
identity-service,inventory-service, and the rest can't be reached directly, even from inside the network — each one only accepts a request that carries a valid, short-lived security token (X-Service-Token, signed withSERVICE_JWT_SECRET).
The result: even if api-gateway were compromised or misconfigured, an attacker still can't walk straight into the credential vault or the identity service — the token check happens again at each one.
zabbix-integration-service is the one exception — it serves /webhook/zabbix/* directly (Zabbix calls it, bypassing api-gateway), so it checks its own secret instead: it refuses to start unless ZABBIX_WEBHOOK_HMAC_SECRET is set.
Data layer
Four databases, one engine (Postgres or MySQL, chosen via DB_ENGINE): seyalrun_identity, seyalrun_inventory, seyalrun_terminal, seyalrun_automation. ops/init-db.sh creates all four and imports the static schema (schema/<engine>/schema.sql) into identity/inventory only — terminal and automation have no static schema; their tables come entirely from each service's own Alembic migrations.
Rate limiting
api-gateway enforces per-IP/user rate limiting backed by Redis (REDIS_URL), configurable via RATE_LIMIT_REQUESTS and RATE_LIMIT_WINDOW_SECONDS.
Why this shape
Splitting identity/inventory/terminal/automation/recording/metrics into separate services means each can be scaled, migrated, and rotated independently, and a vulnerability in one (say, terminal-service, which handles live SSH sessions) doesn't automatically expose the credential vault in inventory-service — the service-token boundary is the actual security control, not just an organizational convenience.