Team Chat¶
Stentor includes a built-in real-time chat system for operator coordination during engagements. Messages are delivered instantly via the CockpitHub WebSocket, appearing in the Event Log tab alongside beacon events and system notifications. The chat supports public messages, private messages, action messages, and operator presence queries.
Architecture¶
Chat messages flow through the REST API to the CockpitHub WebSocket, which broadcasts them to connected operator clients in real time. There is no persistent message storage -- chat is ephemeral and scoped to the current session.
sequenceDiagram
participant Op1 as Operator A
participant API as REST API
participant Hub as CockpitHub<br/>(WebSocket)
participant Op2 as Operator B
participant Op3 as Operator C
Op1->>API: POST /cockpit/chat<br/>{"message": "Got DA creds"}
API->>Hub: BroadcastConsoleLog
Hub-->>Op1: console_log event
Hub-->>Op2: console_log event
Hub-->>Op3: console_log event Sending Messages¶
All chat messages are sent via the same endpoint. The message content determines whether it is a public broadcast, private message, action, or query.
Public Message¶
A regular chat message broadcast to all connected operators.
All connected operators receive a WebSocket event:
{
"type": "console_log",
"payload": {
"tab_id": "event-log",
"timestamp": "14:30:05",
"type": "chat",
"text": "<[email protected]> Starting lateral movement to DC01"
}
}
Private Message¶
Send a message visible only to a specific operator using the /msg command prefix.
curl -s -X POST https://stentor.app/api/v1/cockpit/chat \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "/msg [email protected] Check the creds I just dumped"
}' | jq
{
"error": "operator \"[email protected]\" not connected"
}
The private message is delivered to both the sender and the recipient. Other connected operators do not see it.
Format: /msg <recipient-email> <message text>
Recipient must be connected
Private messages can only be delivered to operators with an active WebSocket connection. If the recipient is not connected, the API returns a 404 error.
Action Message¶
Send an action message (similar to IRC /me) that displays as a status line rather than a chat bubble.
All connected operators receive:
{
"type": "console_log",
"payload": {
"tab_id": "event-log",
"timestamp": "14:32:10",
"type": "info",
"text": "*** [email protected] is escalating privileges on WS01"
}
}
Format: /me <action text>
List Connected Operators¶
Query the list of currently connected operators. This message is only sent back to the requesting operator -- it is not broadcast.
The requesting operator receives a private console log entry:
{
"type": "console_log",
"payload": {
"tab_id": "event-log",
"timestamp": "14:33:00",
"type": "info",
"text": "Connected operators: [email protected], [email protected]"
}
}
Chat Commands Summary¶
| Command | Syntax | Visibility | Description |
|---|---|---|---|
| Public | <message> | All operators | Broadcast to everyone |
| Private | /msg <email> <text> | Sender + recipient | Direct message to one operator |
| Action | /me <text> | All operators | Status/action message |
| Names | /names | Sender only | List connected operators |
Operator Presence¶
List Connected Operators (REST)¶
Query the list of currently connected operator emails via a dedicated REST endpoint, independent of the chat system.
{
"operators": [
"[email protected]",
"[email protected]",
"[email protected]"
],
"count": 3
}
Response fields:
| Field | Type | Description |
|---|---|---|
operators | string[] | Email addresses of connected operators |
count | int | Number of connected operators |
Automatic polling
The Stentor UI polls the operators endpoint every 15 seconds to keep the connected operators list current.
WebSocket Integration¶
Chat messages are delivered through the CockpitHub WebSocket at /api/v1/cockpit/ws. This is the same WebSocket used for beacon updates, task completions, and other real-time events. Chat messages are distinguished by their event structure:
| Field | Value | Description |
|---|---|---|
type | console_log | Event type (same as shell output and system messages) |
payload.tab_id | event-log | Always delivered to the Event Log tab |
payload.type | chat or info | chat for public/private messages, info for actions and /names |
payload.timestamp | HH:MM:SS | Server-side timestamp when the message was processed |
payload.text | string | Formatted message text |
Message text formats:
| Chat Type | Text Format |
|---|---|
| Public | <sender@email> message text |
| Private | [private] <sender@email -> recipient@email> message text |
| Action | *** sender@email action text |
| Names | Connected operators: email1, email2, ... |
Event Log Tab¶
Chat messages appear in the Event Log tab in the cockpit console alongside other operational events (beacon check-ins, task completions, errors). This unified timeline gives operators a chronological view of both system activity and team communication.
The Event Log is the default tab and is always available. Chat messages are interleaved with:
- Beacon registration and check-in notifications
- Task queuing and completion events
- Listener start/stop notifications
- System errors and warnings
Dedicated chat input
The cockpit console input can be used for both beacon shell commands (when a beacon tab is active) and chat messages (when the Event Log tab is active). Switch to the Event Log tab to type chat messages.
API Summary¶
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/cockpit/chat | Send a chat message (public, private, action, or names query) |
GET | /api/v1/cockpit/operators | List currently connected operators |
GET | /api/v1/cockpit/ws | WebSocket endpoint for real-time events (including chat delivery) |