- Scope — what ships, in tiers
- How it works — install and run locally
- Architecture — hexagonal, where things live
- Roadmap — project milestones
- Contributing — workflow and Definition of Done
OpsWarden is a platform where a technical team coordinates, in real time, its Incidents (unplanned problems, triaged and resolved) and its Releases (deployments validated step by step). The two are linked: an active incident can block an in-progress release.
External events can automatically trigger internal actions through an Action→REAction rule engine: the current implementation live-proves a signed GitHub CI failure webhook creating an incident. GitLab and an AI SRE investigation agent remain roadmap items rather than shipped alpha features.
Positioning: a publishable mini incident.io / Rootly focused on reducing MTTR, rather than yet another re-skinned real-time chat. All business logic lives on the server (Rust/Axum, hexagonal architecture); the web and desktop clients display and relay, with no business logic.
Status: the alpha web product is implemented and tested — email/JWT auth (with logout/revocation), teams + 3-role RBAC, incident lifecycle, real-time roster presence, timeline editing, emoji reactions, member moderation, private messages, and GIPHY-powered GIF timeline entries, all on PostgreSQL (SQLx). Release management is implemented with step validation and automatic blocking by linked incidents. Desktop is partially implemented as a Tauri URL-mode shell with tray/background behavior and native assignment, high-severity, and
release_blockednotifications. Compose builds an installable Linux.deband serves it over HTTP throughclient_web; the AppImage is built by the release CI on a GitHububuntu-22.04runner and is exposed as/client.AppImagewhen copied into./artifacts.
OpsWarden aims to be a real Incident Management Platform, in the lineage of PagerDuty, Opsgenie, incident.io, Rootly and Datadog Incident Management, delivered in tiers. Locked architecture decision: a modular hexagonal monolith (cargo + npm workspaces) for the core, a single extracted service (the AI SRE agent, behind a port), and the cloud/ops layer in separate repositories — the microservices instinct is honored where it pays, without distributed-systems tax.
Core Features
- Email auth + JWT,
/me, logout with token invalidation; teams + 3-role RBAC (Observer / Responder / Manager) + invitation code + Manager transfer - Incidents (open → acknowledged → escalated → resolved, severities) with a real-time collaborative timeline, inline edits and emoji reactions
- WebSockets (
incident_*,presence_update) + automatic client reconnection - Action→REAction automation: GitHub webhook (CI failed) → incident;
dynamic
/about.json+ SHA-256 token; encrypted token vault (AES-GCM) - Team member moderation: kick, temporary ban, permanent ban, ban-gated rejoin
- Private messages between users sharing a team, delivered over a user-scoped WebSocket event
- GIPHY GIF search via a server-side API key and authenticated backend proxy
- Releases with ordered step validation and automatic blocking/unblocking by linked incident state
docker-composefor server + db; local web via npm/Next.js; GitHub Actions CI/CD; FR/EN i18n
Extended Features (in progress / planned)
- Tauri desktop URL-mode shell is present (OS notifications + tray); Compose
builds a local
.debserved byclient_web, and the release CI builds the AppImage artifact for the canonical/client.AppImagedownload path - Google OAuth2 exists as optional auth plumbing
- GitLab as an Action; additional REActions (Slack / HTTP / Email)
Long-term vision
- AI SRE: RAG microservice (FastAPI,
@ask/@search, pgvector, LLM/SLM) correlating logs + commit diff + past incidents to propose a root cause + runbook - Integrations: Slack, Jira / Confluence
- Observability: OpenTelemetry + Prometheus + Grafana + Loki + Promtail
- IaC showcase (repo
opswarden-ops): Minikube → k8s → Terraform → DigitalOcean (DOKS) + Traefik + cAdvisor + Argo/Flux; Redis + async workers - Deployment: Vercel (web) + multi-repo (product monorepo v1, separate ops repos)
# 1. Clone
git clone https://github.com/opswarden-git/opswarden.git # HTTPS
git clone git@github.com:opswarden-git/opswarden.git # SSH
cd opswarden
# 2. Configure the environment
cp .env.example .env
# adjust OPSWARDEN_KICKOFF_TOKEN and DATABASE_URL if needed
# 3. Run everything (database + server + desktop artifact + web UI)
docker compose up --buildCompose brings up db, the server on :8080, a build-only client_desktop
service that deposits the Linux desktop package in ./artifacts, and the
production client_web on :8081 (the Next.js UI, also the URL-mode target
the desktop build loads). client_web proxies /api/* to the server over the
compose network; the browser reaches the WebSocket directly on the server's
published :8080. If host port 8081 is already in use, run
CLIENT_WEB_PORT=8091 docker compose up --build; the container still listens on
:8081 internally.
Check the services respond:
curl http://localhost:8080/health # -> {"status":"ok"}
curl http://localhost:8080/about.json # -> service catalog + SHA-256 token
curl http://localhost:8081/en # -> 200, the web UI (FR at /fr)
curl -I http://localhost:8081/client.debThe desktop shell loads the web UI from http://localhost:8081, so it needs the
compose stack (or a dev server) running. In dev: just desktop-dev.
A build-only client_desktop compose service builds an installable Linux package
in an Ubuntu/FHS container (the Tauri bundler can't run on a NixOS host) and drops
it on the host under ./artifacts. client_web depends on that build and exposes
the package over HTTP:
docker compose up --build
curl -I http://localhost:8081/client.deb
sudo apt install ./artifacts/OpsWarden_amd64.debThe AppImage is produced by the release CI, not by local Docker: Tauri's
pinned linuxdeploy is broken inside a local container, while GitHub's
ubuntu-22.04 runner (tauri-action) builds it successfully. On a v*.*.* tag
the Release workflow attaches it to the GitHub Release; a manual run
(Run workflow / workflow_dispatch) uploads it as the opswarden-appimage
artifact. Place the downloaded AppImage at ./artifacts/client.AppImage and the
running web container serves the VIGIL canonical route:
curl -I http://localhost:8081/client.AppImageopswarden/
├── server/ # Rust/Axum -- ALL business logic (hexagonal)
│ ├── src/
│ │ ├── domain/ # pure models (Incident, Team, Timeline...) -- zero I/O
│ │ ├── ports/ # traits (IncidentRepo, EventBus, TokenVault...)
│ │ ├── app/ # use-cases (business rule orchestration)
│ │ ├── adapters/ # port implementations (Postgres, WS, crypto)
│ │ ├── handlers/ # Axum routes + WebSocket upgrade (no logic)
│ │ ├── config.rs
│ │ └── lib.rs # build_app(): app testable without opening a socket
│ ├── tests/ # integration tests
│ └── Dockerfile # multi-stage build of the server binary
├── client-web/ # Next.js + Tailwind -- supervision UI
├── client-desktop/ # Tauri -- URL-mode native app + tray (alpha)
├── investigation/ # AI SRE agent (RAG / pgvector) (planned, not in repo yet)
├── .github/workflows/ # server + web + release CI
├── docker-compose.yml # compose: db + server + client_desktop + client_web
├── Cargo.toml # cargo workspace
└── package.json # npm workspaces
# Server (Rust)
cd server
cargo run # http://localhost:8080
cargo test # unit + integration tests
cargo clippy --all-targets -- -D warnings # lint
cargo fmt # format
# Web client (Next.js, from the root via npm workspaces)
npm install
npm run dev --workspace client-web # http://localhost:4242 (compose exposes 8081)
npm run build --workspace client-web| Service | Stack | Local address |
|---|---|---|
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/postgresql/postgresql-original.svg" height="18" /> db |
PostgreSQL | localhost:5433 |
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/rust/rust-original.svg" height="18" /> server |
Rust / Axum | http://localhost:8080 |
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/nextjs/nextjs-original.svg" height="18" /> client_web |
Next.js | http://localhost:4242 |
<img src="https://api.iconify.design/simple-icons/tauri.svg" height="18" /> client_desktop |
Tauri | URL mode via just desktop-dev |
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg" height="18" /> investigation |
AI SRE (RAG) | internal |
Cloud showcase (separate opswarden-ops repo):
Hexagonal dependency rule: everything points inward. The domain knows nothing about Axum, SQLx, or the network.
handlers (Axum, WS) -> app (use-cases) -> ports (traits) -> domain (pure)
^
adapters (Postgres, WS broadcaster, vault) implement the ports
- Where business logic lives:
server/src/domain(models + invariants) andserver/src/app(use-cases). Never in handlers or clients. - Where routes are wired:
server/src/handlers+build_app()inserver/src/lib.rs. - Where persistence happens:
server/src/adapters(port implementations). - Where the WebSocket broadcaster lives: an adapter implementing the
EventBusport.
Foundations & rails
- Scaffold monorepo: cargo workspace (
server) + npm workspaces (client-web) - Hexagonal skeleton
domain / ports / app / adapters / handlers+GET /health - Dynamic
/about.json+ SHA-256tokenfield (kickoff string) - Green CI quality gate:
cargo fmt --check,clippy -D warnings, ESLint,prettier --checkpass on every push
Real-time collaborative core
- Email auth + JWT,
GET /me, logout with token invalidation - Teams + 3-role RBAC + invitation code + Manager transfer
- Incidents: open → acknowledged → escalated → resolved lifecycle + severities
- Real-time collaborative timeline (timestamped entries, Responder assignment)
- Core WebSockets:
incident_state_changed,incident_escalated,incident_assigned,timeline_entry_added,presence_update+ automatic client reconnection - Postgres persistence (SQLx) + versioned migrations
Automation & professionalization
- Webhook receiver
POST /webhooks/{service}+ HMAC validation - Hook engine (trigger + filters → reaction); 1 end-to-end rule: failing GitHub CI →
highincident - 1 external Action (GitHub) + 1 REAction (generic HTTP
Notify, covers Slack) /about.jsonreflects the real catalog (nothing hard-coded client-side)- WebSockets
rule_triggered,rule_failed
Desktop & delivery
- Tauri URL-mode shell reusing the front-end, with tray/background behavior
- Native OS notifications: assignment, high/critical severity, and blocked Release are live-proven
- Compose covers
db/server8080 / build-onlyclient_desktop/client_web8081. The web client serves the local.debat/client.deband the CI-built AppImage at/client.AppImagewhen placed in./artifacts/client.AppImage - FR/EN i18n (labels, states, severities) persisted server-side
Trunk-based workflow: short-lived branches (feat/, fix/, chore/, docs/,
test/), conventional commits, squash-merge into a protected main. Every PR
follows the PR template, whose Definition of
Done requires: clippy -D warnings and cargo fmt --check green, npm run lint
format:check+typecheckgreen, tests covering the happy path and at least one error path, business logic kept out of handlers and clients, impacted docs updated, and an atomic conventional commit.
OpsWarden is distributed under the Apache License 2.0. See LICENSE and NOTICE.