Skip to content

Latest commit

Β 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AIOps Platform Agent

CI .NET C# License: MIT Platform

A cross-platform .NET 8 endpoint agent that runs as a Windows Service or Linux systemd service. It implements a custom Agent-to-Agent (A2A) protocol, registering with a central orchestrator and accepting task delegation for server management and security scanning capabilities.


Table of Contents


Architecture

The solution contains two assemblies:

Project Role
AIOpsAgent.Core Platform abstraction, A2A protocol models, JWT auth, HTTP client, capability handlers, security detectors
AIOpsAgent ASP.NET Core minimal-API host, DI wiring, HTTP endpoints, lifecycle and task services
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AIOps Orchestrator                                          β”‚
β”‚  POST /api/a2a/register  ←──  agent registers on startup    β”‚
β”‚  POST /api/a2a/delegate  ──►  orchestrator sends tasks       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                   A2A over HTTP
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AIOps Platform Agent  (this repo)                           β”‚
β”‚                                                              β”‚
β”‚  ASP.NET Core Host                                           β”‚
β”‚  β”œβ”€β”€ /api/a2a/delegate    ← receive task                     β”‚
β”‚  β”œβ”€β”€ /api/a2a/tasks/{id}  ← poll status                     β”‚
β”‚  β”œβ”€β”€ /api/a2a/webhook     ← receive completion callback     β”‚
β”‚  β”œβ”€β”€ /health              ← liveness probe                   β”‚
β”‚  └── /ready               ← readiness probe                  β”‚
β”‚                                                              β”‚
β”‚  Services                                                    β”‚
β”‚  β”œβ”€β”€ AgentLifecycleService  (startup register / deregister)  β”‚
β”‚  └── TaskExecutorService    (dispatch, track, timeout)       β”‚
β”‚                                                              β”‚
β”‚  Core Library                                                β”‚
β”‚  β”œβ”€β”€ IPlatformProvider                                       β”‚
β”‚  β”‚   β”œβ”€β”€ LinuxPlatformProvider  (/proc, systemctl, df)       β”‚
β”‚  β”‚   └── WindowsPlatformProvider  (WMI, sc.exe, DriveInfo)   β”‚
β”‚  β”œβ”€β”€ ICapabilityHandler implementations                      β”‚
β”‚  β”‚   β”œβ”€β”€ SystemInfoHandler                                   β”‚
β”‚  β”‚   β”œβ”€β”€ ListProcessesHandler                                β”‚
β”‚  β”‚   β”œβ”€β”€ KillProcessHandler                                  β”‚
β”‚  β”‚   β”œβ”€β”€ ListServicesHandler                                 β”‚
β”‚  β”‚   β”œβ”€β”€ ManageServiceHandler                                β”‚
β”‚  β”‚   └── RunCommandHandler                                   β”‚
β”‚  └── ISecurityDetector implementations                       β”‚
β”‚      β”œβ”€β”€ LinuxAuditDetector  (auth.log, audit.log)           β”‚
β”‚      └── WindowsEventLogDetector  (Security, System logs)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Capabilities

Capability string What it does
server.system_info OS info, CPU, memory, disk, network interfaces
server.list_processes Running processes sorted by memory or CPU
server.kill_process Terminate a process by PID
server.list_services All systemd / Windows services with status
server.manage_service Start, stop, or restart a named service
server.run_command Run an allowlisted diagnostic command
security.scan Scan OS logs for auth failures, brute force, privilege escalation

Sequence Diagrams

Agent Startup and Registration

sequenceDiagram
    participant Svc as systemd / Windows SCM
    participant Agent as AIOps Agent
    participant Orch as Orchestrator

    Svc->>Agent: Start service
    Agent->>Agent: Bootstrap Serilog
    Agent->>Agent: Validate AgentOptions (ApiKey, JwtSecret required)
    Agent->>Agent: Detect platform (Linux / Windows)
    Agent->>Agent: Start ASP.NET Core host

    activate Agent
    Agent->>Orch: POST /api/a2a/register\n{agentId, hostname, capabilities, apiKey}
    alt Orchestrator available
        Orch-->>Agent: 200 OK {orchestratorId}
        Agent->>Agent: Log registration success
    else Orchestrator unavailable
        Orch-->>Agent: timeout / error
        Agent->>Agent: Log warning, continue in standalone mode
    end

    Note over Agent: Agent is now ready to accept tasks
    Agent-->>Svc: Service running (ACTIVE)
    deactivate Agent
Loading

Synchronous Task Delegation

sequenceDiagram
    participant Orch as Orchestrator
    participant MW as AgentAuthMiddleware
    participant Exec as TaskExecutorService
    participant Handler as ICapabilityHandler
    participant OS as OS / Platform

    Orch->>+Agent: POST /api/a2a/delegate\nAuthorization: Bearer {JWT}\n{capability, params, mode: sync, timeoutMs}

    Agent->>+MW: ValidateToken(jwt)
    MW-->>-Agent: agentId (or 401)

    Agent->>+Exec: ExecuteAsync(request, ct)
    Exec->>Exec: Generate taskId, store Pending

    Exec->>+Handler: ExecuteAsync(params, ct)
    Handler->>+OS: platform.GetSystemInfoAsync() / etc.
    OS-->>-Handler: result
    Handler-->>-Exec: CapabilityResult

    Exec->>Exec: Store Completed
    Exec-->>-Agent: TaskResult {taskId, status: completed, output}
    Agent-->>-Orch: 200 OK {taskId, status, output}
Loading

Asynchronous Task Delegation

sequenceDiagram
    participant Orch as Orchestrator
    participant Agent as AIOps Agent
    participant Exec as TaskExecutorService
    participant Handler as ICapabilityHandler

    Orch->>+Agent: POST /api/a2a/delegate\n{capability, mode: async, callbackUrl, timeoutMs}
    Agent->>Agent: ValidateToken(jwt)
    Agent->>+Exec: ExecuteAsync(request, ct)
    Exec->>Exec: Generate taskId, store Pending
    Exec->>Exec: Fire-and-forget RunAsync(taskId, handler, request)
    Exec-->>-Agent: TaskResult {taskId, status: pending}
    Agent-->>-Orch: 202 Accepted {taskId, status: pending}

    Note over Exec,Handler: Background execution

    activate Exec
    Exec->>+Handler: ExecuteAsync(params, CancellationToken)
    Handler-->>-Exec: CapabilityResult
    Exec->>Exec: Store Completed
    deactivate Exec

    Note over Orch,Agent: Caller must poll for result\n(outbound webhook NOT yet implemented)

    Orch->>+Agent: GET /api/a2a/tasks/{taskId}
    Agent-->>-Orch: 200 OK {taskId, status: completed, output}
Loading

Security Scan Flow

sequenceDiagram
    participant Orch as Orchestrator
    participant Agent as AIOps Agent
    participant Scan as SecurityScanCapability
    participant Linux as LinuxAuditDetector
    participant Win as WindowsEventLogDetector

    Orch->>+Agent: POST /api/a2a/delegate\n{capability: security.scan, params: {minSeverity}}
    Agent->>+Scan: ExecuteAsync(params, ct)

    loop For each registered ISecurityDetector
        Scan->>+Linux: DetectAsync(ct)
        Linux->>Linux: ReadAllLines(/var/log/auth.log)
        Linux->>Linux: ReadAllLines(/var/log/audit/audit.log)
        Linux->>Linux: Parse SSH failures, sudo, user creation
        Linux->>Linux: Correlate brute-force (>= 5 failures)
        Linux-->>-Scan: List<SecurityEvent>

        Scan->>+Win: DetectAsync(ct)
        Win->>Win: Query Windows Security Event Log\n(4624, 4625, 4648, 4672, 4720, 4740)
        Win->>Win: Query Windows System Event Log\n(7040, 7045)
        Win->>Win: Correlate brute-force
        Win-->>-Scan: List<SecurityEvent>
    end

    Scan->>Scan: Filter by minSeverity
    Scan->>Scan: Order by Timestamp desc
    Scan-->>-Agent: SecurityScanResult {events, summary}
    Agent-->>-Orch: 200 OK {output}
Loading

Agent Shutdown

sequenceDiagram
    participant Svc as systemd / SCM
    participant Agent as AIOps Agent
    participant Orch as Orchestrator

    Svc->>Agent: SIGTERM / Stop command
    Agent->>Agent: IHostedService.StopAsync triggered
    Agent->>Orch: Best-effort deregister
    Orch-->>Agent: Response (or timeout)

    Agent->>Agent: Dispose HttpClient, flush logs
    Agent-->>Svc: Service stopped
Loading

Slack Integration

The AIOps Orchestrator exposes a Slack bot that teams use as the primary control plane. Users interact with the orchestrator directly from Slack; the orchestrator then delegates work to registered platform agents (this repo) over A2A.

How it works

User in Slack
    β”‚  @mention or DM  (e.g. "scan prod-server-01 for security issues")
    β–Ό
Slack Events API  ──POST /api/webhook/slack──►  AIOps Orchestrator
                                                      β”‚
                                        MessageHandler parses intent
                                                      β”‚
                                        TaskDelegator selects agent
                                        (AgentRegistry capability match)
                                                      β”‚
                              A2A  POST /api/a2a/delegate  ──►  AIOps Platform Agent
                                                                      β”‚
                                                           ICapabilityHandler executes
                                                                      β”‚
                              A2A  POST /api/a2a/webhook   ◄──  result / webhook
                                                      β”‚
                                        Orchestrator formats response
                                                      β”‚
                                        Slack Web API chat.postMessage
    β–Ό
User receives answer in Slack

Slack commands supported by the orchestrator

Commands can be prefixed with / or ! (Slack intercepts bare / commands, so ! is the Slack-friendly alias).

Command What it does
!help Show all available commands
!k8s pods [namespace] List pods
!k8s fix [namespace] Auto-remediate CrashLoop / OOMKilled / Error pods
!k8s scale <name> <n> [namespace] Scale a deployment
!k8s logs <pod> [namespace] Tail pod logs
!k8s nodes List cluster nodes
!incident list List open incidents
!alert list List recent alerts
!approval list List pending human-approval requests
approve <id> Approve a pending remediation action
reject <id> Reject a pending remediation action
@agent-name <task> Delegate a task directly to a named platform agent
Natural language Any message not matching a command is handled by the AI router

Human-in-the-loop approvals

High-risk actions (node drain, service stop, process kill) require explicit Slack approval before the orchestrator executes them. The approval request is posted to the same Slack channel with a short ID:

πŸ”΄ Approval Required [HIGH]

Action: Stop service nginx on prod-server-01
Tool: `server.manage_service`
Parameters: `{"name": "nginx", "action": "stop"}`

Reply with approve a1b2c3d4 to proceed or reject a1b2c3d4 to cancel.
This request expires in 15 minutes.

Risk classification:

  • LOW β€” execute immediately, notify after
  • MEDIUM β€” post approval request, user must confirm
  • HIGH β€” post approval request with explicit risk warning; all actions are audit-logged to PostgreSQL

Setting up Slack on the orchestrator side

See the full guide at docs/slack-setup.md in the orchestrator repo. Quick summary:

  1. Go to api.slack.com/apps β†’ Create New App β†’ From a manifest
  2. Paste slack_manifest.yml from the orchestrator repo (update request_url to your deployment URL)
  3. Install to workspace, copy Bot User OAuth Token (xoxb-...) and Signing Secret
  4. Set in the orchestrator's .env:
    SLACK_BOT_TOKEN=xoxb-your-token
    SLACK_SIGNING_SECRET=your-signing-secret
  5. Invite the bot to channels: /invite @aiops-orchestrator

Incident Flow β€” Slack to Agent

Normal operation: user-initiated task

sequenceDiagram
    actor User as Slack User
    participant Slack as Slack
    participant Orch as AIOps Orchestrator
    participant Reg as AgentRegistry (Redis+PG)
    participant Agent as AIOps Platform Agent

    User->>Slack: "scan prod-server-01 for security issues"
    Slack->>Orch: POST /api/webhook/slack\n{event: app_mention, text: ...}
    Orch->>Orch: SlackAdapter.parse_message()
    Orch->>Orch: MessageHandler detects capability:\nsecurity.scan
    Orch->>Reg: find_agents_by_capability("security.scan")
    Reg-->>Orch: [aiops-agent-prod-01, score=1.0]
    Orch->>Agent: POST /api/a2a/delegate\nBearer JWT\n{capability: "security.scan", params: {min_severity: "Medium"}}
    Agent->>Agent: LinuxAuditDetector + WindowsEventLogDetector run
    Agent-->>Orch: 200 OK {taskId, status: completed, result: {...events}}
    Orch->>Orch: Format security scan results
    Orch->>Slack: chat.postMessage β†’ summary of events
    Slack-->>User: Security scan results posted in channel
Loading

High-risk action requiring approval

sequenceDiagram
    actor User as Slack User
    participant Slack as Slack
    participant Orch as AIOps Orchestrator
    participant Approval as ApprovalManager (Redis)
    participant Agent as AIOps Platform Agent

    User->>Slack: "stop nginx on prod-server-01"
    Slack->>Orch: POST /api/webhook/slack
    Orch->>Orch: Detect intent: server.manage_service\nRisk level: HIGH
    Orch->>Approval: request_approval(tool="server.manage_service",\nrisk=HIGH, ttl=15min)
    Approval->>Slack: chat.postMessage\n"πŸ”΄ Approval Required β€” reply approve a1b2c3d4"
    Slack-->>User: Approval request posted

    Note over User,Slack: User reviews and decides

    User->>Slack: "approve a1b2c3d4"
    Slack->>Orch: POST /api/webhook/slack
    Orch->>Approval: process_response("approve a1b2c3d4", user_id)
    Approval->>Approval: Lookup in Redis, verify pending
    Approval->>Agent: POST /api/a2a/delegate\n{capability: "server.manage_service",\nparams: {name: "nginx", action: "stop"}}
    Agent->>Agent: ManageServiceHandler.ExecuteAsync()
    Agent-->>Orch: 200 OK {status: completed}
    Approval->>Approval: Write audit log to PostgreSQL
    Orch->>Slack: chat.postMessage "βœ… nginx stopped on prod-server-01"
    Slack-->>User: Confirmation posted
Loading

Automated incident detection and remediation

sequenceDiagram
    participant Watch as Orchestrator WatchLoop
    participant Orch as AIOps Orchestrator
    participant Agent as AIOps Platform Agent
    participant Slack as Slack

    Note over Watch: Periodic health check cycle
    Watch->>Agent: GET /health
    Agent-->>Watch: 200 {status: online, activeTasks: 0}

    Note over Watch: Anomaly detected via monitoring
    Watch->>Orch: Alert: high CPU on prod-server-01
    Orch->>Agent: POST /api/a2a/delegate\n{capability: "server.list_processes",\nparams: {sort_by: "cpu", limit: 5}}
    Agent-->>Orch: Top 5 CPU processes

    Orch->>Orch: RCA Engine: identify runaway process
    Orch->>Slack: chat.postMessage\n"⚠️ Alert: prod-server-01 CPU 94%\nTop process: java PID 4521 (87%)\nApproval required to kill"

    Note over Slack: On-call engineer responds
    Slack-->>Orch: "approve <id>"
    Orch->>Agent: POST /api/a2a/delegate\n{capability: "server.kill_process", params: {pid: 4521}}
    Agent-->>Orch: {status: completed, killed: true}
    Orch->>Slack: "βœ… PID 4521 killed. CPU normalising."
Loading

Configuration

Configuration is layered in priority order:

appsettings.json
  └── appsettings.{ASPNETCORE_ENVIRONMENT}.json
        └── Environment variables (prefix: AIOPS_)
              └── Command-line args

Key options (Agent: section)

Key Required Default Notes
AgentId No hostname Unique identifier for this agent
ApiKey Yes β€” Min 32 chars. Set via env var AIOPS_Agent__ApiKey
JwtSecret Yes β€” Min 32 chars. Set via env var AIOPS_Agent__JwtSecret
OrchestratorUrl No "" Leave empty for standalone mode
ListenUrl No http://0.0.0.0:8090 HTTP listen address
AllowedCapabilities No ["*"] Allowlist of capability strings
LookbackHours No 1 Security scan lookback window
HealthCheckIntervalSeconds No 60 Heartbeat interval to orchestrator
EnableJsonLogging No false Enable JSON-format log output

Secrets (Linux)

Set via /etc/aiops-agent/environment (mode 0600, loaded by systemd EnvironmentFile):

AIOPS_Agent__ApiKey=<min-32-char-random-string>
AIOPS_Agent__JwtSecret=<min-32-char-random-string>

Secrets (Windows)

Set via System Environment Variables after installation:

[System.Environment]::SetEnvironmentVariable("AIOPS_Agent__ApiKey", "<key>", "Machine")
[System.Environment]::SetEnvironmentVariable("AIOPS_Agent__JwtSecret", "<secret>", "Machine")

Deployment

Prerequisites

  • .NET 8 Runtime (self-contained publish includes runtime β€” no separate install needed)
  • Linux: systemd, audit group membership for /var/log/audit/audit.log access
  • Windows: Administrator rights for service installation

Linux

# Build self-contained binary
make publish-linux-x64

# Run installer (requires root)
sudo bash installer/linux/install.sh

# Verify
systemctl status aiops-agent
curl http://localhost:5000/ready

Windows (MSI)

# Build MSI
make msi

# Silent install
.\installer\windows\silent-install.ps1 `
    -MsiPath ".\publish\AIOpsAgent-1.0.0-win-x64.msi" `
    -ApiKey "<key>" `
    -JwtSecret "<secret>" `
    -ListenPort 5000

Build targets

make build           # Debug build
make test            # Run unit tests
make publish-all     # Publish all 4 RIDs (linux-x64, linux-arm64, win-x64, win-x86)
make msi             # Build Windows MSI (requires WiX toolset)
make clean           # Remove bin/obj/publish

HTTPS / TLS

By default the agent listens on plain HTTP. All A2A traffic β€” including the Bearer JWT β€” is sent in cleartext unless TLS is in place. Two supported approaches:

Option A β€” Reverse proxy (recommended for production)

Place nginx, Caddy, or an IIS ARR proxy in front of the agent. The agent binds http://127.0.0.1:8090 (loopback only) and the proxy terminates TLS externally.

Linux (nginx example):

server {
    listen 443 ssl;
    server_name agent.example.internal;

    ssl_certificate     /etc/ssl/agent.crt;
    ssl_certificate_key /etc/ssl/agent.key;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
    }
}

Change ListenUrl to http://127.0.0.1:8090 so the agent only accepts local connections:

"Agent": { "ListenUrl": "http://127.0.0.1:8090" }

Option B β€” Kestrel native TLS

Configure Kestrel directly in appsettings.json. Requires a PFX certificate (self-signed or CA-issued):

"Kestrel": {
  "Endpoints": {
    "Https": {
      "Url": "https://0.0.0.0:8443",
      "Certificate": {
        "Path": "/etc/aiops-agent/agent.pfx",
        "Password": "<pfx-password>"
      }
    }
  }
}

Update Agent:ListenUrl to match so the agent registers its correct address with the orchestrator:

"Agent": { "ListenUrl": "https://<hostname>:8443" }

The orchestrator and any caller must then use https:// URLs. The HttpClient in A2AClient inherits the system trust store; add your CA to the OS trust store if using a private CA.


License

MIT License β€” see LICENSE.

Copyright (c) 2026 AIOps Orchestrator Contributors

About

A cross-platform .NET 8 endpoint agent that runs as a Windows Service or Linux systemd service. It implements a custom Agent-to-Agent (A2A) protocol, registering with a central orchestrator and accepting task delegation for server management and security scanning capabilities.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages