@simbimbo/brainstem 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +26 -0
- package/brainstem/__init__.py +1 -1
- package/brainstem/adapters.py +120 -0
- package/brainstem/api.py +468 -57
- package/brainstem/config.py +136 -0
- package/brainstem/connectors/logicmonitor.py +57 -0
- package/brainstem/demo.py +16 -2
- package/brainstem/fingerprint.py +54 -0
- package/brainstem/ingest.py +440 -33
- package/brainstem/interesting.py +56 -1
- package/brainstem/listener.py +181 -0
- package/brainstem/models.py +1 -0
- package/brainstem/recurrence.py +63 -9
- package/brainstem/scoring.py +6 -4
- package/brainstem/source_drivers.py +179 -0
- package/brainstem/storage.py +389 -12
- package/docs/README.md +103 -0
- package/docs/api.md +260 -280
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/tests/test_adapters.py +95 -0
- package/tests/test_api.py +812 -0
- package/tests/test_canonicalization.py +8 -0
- package/tests/test_config.py +39 -0
- package/tests/test_file_ingest.py +77 -0
- package/tests/test_fingerprint.py +51 -1
- package/tests/test_interesting.py +10 -0
- package/tests/test_listener.py +253 -0
- package/tests/test_logicmonitor.py +54 -1
- package/tests/test_recurrence.py +16 -0
- package/tests/test_source_drivers.py +95 -0
- package/tests/test_storage.py +178 -1
package/docs/api.md
CHANGED
|
@@ -1,380 +1,360 @@
|
|
|
1
|
-
# API
|
|
1
|
+
# Runtime API (Current Implementation)
|
|
2
|
+
|
|
3
|
+
This document reflects the runtime and API surfaces that are implemented today.
|
|
4
|
+
|
|
5
|
+
## What is exposed today
|
|
6
|
+
|
|
7
|
+
- ingest endpoints
|
|
8
|
+
- `POST /ingest/event`
|
|
9
|
+
- `POST /ingest/batch`
|
|
10
|
+
- `POST /ingest/logicmonitor`
|
|
11
|
+
- `POST /replay/raw`
|
|
12
|
+
- inspection endpoints
|
|
13
|
+
- `GET /interesting`
|
|
14
|
+
- `GET /candidates`
|
|
15
|
+
- `GET /signatures`
|
|
16
|
+
- `GET /canonical_events`
|
|
17
|
+
- `GET /stats`
|
|
18
|
+
- `GET /failures`
|
|
19
|
+
- `GET /failures/{raw_envelope_id}`
|
|
20
|
+
- `GET /raw_envelopes`
|
|
21
|
+
- `GET /ingest/recent`
|
|
22
|
+
- `GET /sources`
|
|
23
|
+
- `GET /sources/status`
|
|
24
|
+
- runtime state endpoints
|
|
25
|
+
- `GET /runtime`
|
|
26
|
+
- `GET /status`
|
|
27
|
+
- `GET /healthz`
|
|
28
|
+
|
|
29
|
+
## Runtime auth
|
|
30
|
+
|
|
31
|
+
`BRAINSTEM_API_TOKEN` controls API auth.
|
|
32
|
+
- if unset: write and read endpoints are open
|
|
33
|
+
- if set: endpoints above require one of:
|
|
34
|
+
- `X-API-Token: <token>`
|
|
35
|
+
- `Authorization: Bearer <token>`
|
|
36
|
+
|
|
37
|
+
`/healthz`, `/runtime`, and `/status` are currently unauthenticated.
|
|
38
|
+
|
|
39
|
+
## Source surface used by the API
|
|
40
|
+
|
|
41
|
+
Registered source types in this milestone are:
|
|
42
|
+
- `syslog`
|
|
43
|
+
- `file`
|
|
44
|
+
- `logicmonitor`
|
|
45
|
+
|
|
46
|
+
Use `source_type` to select `syslog` or `file` payload drivers for raw envelope ingestion.
|
|
47
|
+
`logicmonitor` payloads are expected on `/ingest/logicmonitor`.
|
|
48
|
+
|
|
49
|
+
## Shared ingest request fields
|
|
50
|
+
|
|
51
|
+
`POST /ingest/event` payload:
|
|
2
52
|
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Provide a small, explainable API for ingesting events, scoring patterns, promoting memory, and retrieving relevant operational history.
|
|
6
|
-
|
|
7
|
-
The API should support both machine-driven ingestion and operator-driven investigation.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 1. Ingest Event
|
|
12
|
-
|
|
13
|
-
### `POST /events/ingest`
|
|
14
|
-
|
|
15
|
-
Ingest one normalized or semi-normalized event.
|
|
16
|
-
|
|
17
|
-
Request:
|
|
18
53
|
```json
|
|
19
54
|
{
|
|
20
|
-
"tenant_id": "
|
|
21
|
-
"asset_id": "fw-01",
|
|
55
|
+
"tenant_id": "demo-tenant",
|
|
22
56
|
"source_type": "syslog",
|
|
57
|
+
"source_id": "fw-01",
|
|
58
|
+
"source_name": "edge-fw-01",
|
|
23
59
|
"source_path": "/var/log/syslog",
|
|
60
|
+
"timestamp": "2026-03-22T03:00:00Z",
|
|
24
61
|
"host": "fw-01",
|
|
25
62
|
"service": "charon",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
63
|
+
"severity": "info",
|
|
64
|
+
"asset_id": "fw-01",
|
|
28
65
|
"facility": "daemon",
|
|
29
66
|
"message_raw": "IPsec SA rekey failed; retrying",
|
|
30
|
-
"structured_fields": {
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Response:
|
|
37
|
-
```json
|
|
38
|
-
{
|
|
39
|
-
"ok": true,
|
|
40
|
-
"event_id": "evt_123",
|
|
41
|
-
"signature_id": "sig_456",
|
|
42
|
-
"candidate_ids": ["cand_789"]
|
|
67
|
+
"structured_fields": { "peer": "203.0.113.10" },
|
|
68
|
+
"correlation_keys": {},
|
|
69
|
+
"metadata": {}
|
|
43
70
|
}
|
|
44
71
|
```
|
|
45
72
|
|
|
46
|
-
|
|
73
|
+
Optional query params:
|
|
74
|
+
- `threshold` (default: `2`)
|
|
75
|
+
- `db_path` (default: `BRAINSTEM_DB_PATH` or `.brainstem-state/brainstem.sqlite3`)
|
|
47
76
|
|
|
48
|
-
|
|
77
|
+
The response includes ingest accounting for this request:
|
|
49
78
|
|
|
50
|
-
### `POST /events/ingest_bulk`
|
|
51
|
-
|
|
52
|
-
For log file batches, webhook batches, or replay.
|
|
53
|
-
|
|
54
|
-
Request:
|
|
55
|
-
```json
|
|
56
|
-
{
|
|
57
|
-
"events": [ ... ],
|
|
58
|
-
"source_label": "syslog-replay"
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Response:
|
|
63
79
|
```json
|
|
64
80
|
{
|
|
65
81
|
"ok": true,
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
82
|
+
"tenant_id": "demo-tenant",
|
|
83
|
+
"event_count": 1,
|
|
84
|
+
"signature_count": 1,
|
|
85
|
+
"candidate_count": 0,
|
|
86
|
+
"parse_failed": 0,
|
|
87
|
+
"interesting_items": []
|
|
69
88
|
}
|
|
70
89
|
```
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
## 3. Search Events
|
|
91
|
+
`POST /ingest/batch` accepts:
|
|
75
92
|
|
|
76
|
-
### `POST /events/search`
|
|
77
|
-
|
|
78
|
-
Search raw/normalized events.
|
|
79
|
-
|
|
80
|
-
Request:
|
|
81
93
|
```json
|
|
82
94
|
{
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
"threshold": 2,
|
|
96
|
+
"db_path": "/tmp/brainstem.sqlite3",
|
|
97
|
+
"events": [
|
|
98
|
+
{
|
|
99
|
+
"tenant_id": "demo-tenant",
|
|
100
|
+
"source_type": "syslog",
|
|
101
|
+
"message_raw": "Mar 22 03:00:00 fw-01 charon: VPN tunnel recovered",
|
|
102
|
+
"host": "fw-01",
|
|
103
|
+
"service": "charon"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"tenant_id": "demo-tenant",
|
|
107
|
+
"source_type": "syslog",
|
|
108
|
+
"message_raw": "Mar 22 03:00:10 fw-01 charon: VPN tunnel recovered",
|
|
109
|
+
"host": "fw-01",
|
|
110
|
+
"service": "charon"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
97
113
|
}
|
|
98
114
|
```
|
|
99
115
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
## 4. Search Candidates
|
|
116
|
+
Response shape is the same as single-event ingest, with counts based on all batch rows.
|
|
103
117
|
|
|
104
|
-
|
|
118
|
+
`POST /ingest/logicmonitor` accepts:
|
|
105
119
|
|
|
106
|
-
Search meaningful derived patterns.
|
|
107
|
-
|
|
108
|
-
Request:
|
|
109
120
|
```json
|
|
110
121
|
{
|
|
111
|
-
"tenant_id": "
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
"tenant_id": "demo-tenant",
|
|
123
|
+
"source_path": "/logicmonitor/ingest",
|
|
124
|
+
"threshold": 2,
|
|
125
|
+
"db_path": "/tmp/brainstem.sqlite3",
|
|
126
|
+
"events": [
|
|
127
|
+
{
|
|
128
|
+
"resource_id": 123,
|
|
129
|
+
"resource_name": "edge-fw-01",
|
|
130
|
+
"message_raw": "VPN tunnel dropped and recovered",
|
|
131
|
+
"severity": "warning",
|
|
132
|
+
"metadata": {
|
|
133
|
+
"datasource": "IPSec Tunnel"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
123
137
|
}
|
|
124
138
|
```
|
|
125
139
|
|
|
126
|
-
|
|
140
|
+
Response shape is the same as other ingest endpoints.
|
|
127
141
|
|
|
128
|
-
##
|
|
142
|
+
## Replay raw envelopes
|
|
129
143
|
|
|
130
|
-
|
|
144
|
+
`POST /replay/raw` requires a DB-backed set of raw envelope IDs:
|
|
131
145
|
|
|
132
|
-
Request:
|
|
133
146
|
```json
|
|
134
147
|
{
|
|
135
|
-
"
|
|
148
|
+
"db_path": "/tmp/brainstem.sqlite3",
|
|
149
|
+
"raw_envelope_ids": [1, 2, 5],
|
|
150
|
+
"threshold": 2,
|
|
151
|
+
"force": false,
|
|
152
|
+
"allowed_statuses": ["received", "parse_failed"]
|
|
136
153
|
}
|
|
137
154
|
```
|
|
138
155
|
|
|
139
|
-
|
|
156
|
+
Valid status values are:
|
|
157
|
+
- `received`
|
|
158
|
+
- `canonicalized`
|
|
159
|
+
- `parse_failed`
|
|
160
|
+
- `unsupported`
|
|
161
|
+
|
|
162
|
+
Response example:
|
|
163
|
+
|
|
140
164
|
```json
|
|
141
165
|
{
|
|
142
166
|
"ok": true,
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
"
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
},
|
|
154
|
-
"explanation": "This recurring self-healing VPN issue has increased in frequency and matched a prior promoted incident."
|
|
167
|
+
"db_path": "/tmp/brainstem.sqlite3",
|
|
168
|
+
"requested_raw_envelope_ids": [1,2,5],
|
|
169
|
+
"attempted_raw_envelope_ids": [1,2],
|
|
170
|
+
"skipped": [
|
|
171
|
+
{"raw_envelope_id": 5, "reason": "not_replayable", "status": "canonicalized"}
|
|
172
|
+
],
|
|
173
|
+
"event_count": 2,
|
|
174
|
+
"signature_count": 1,
|
|
175
|
+
"candidate_count": 1,
|
|
176
|
+
"parse_failed": 0
|
|
155
177
|
}
|
|
156
178
|
```
|
|
157
179
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
## 6. Promote Candidate
|
|
180
|
+
## Inspection endpoints
|
|
161
181
|
|
|
162
|
-
### `
|
|
182
|
+
### `GET /interesting`
|
|
163
183
|
|
|
164
|
-
|
|
184
|
+
Query:
|
|
185
|
+
- `limit` (default: `5`)
|
|
186
|
+
- `db_path` (required for non-empty output)
|
|
165
187
|
|
|
166
|
-
|
|
188
|
+
Returns:
|
|
167
189
|
```json
|
|
168
|
-
{
|
|
169
|
-
"candidate_id": "cand_789",
|
|
170
|
-
"title": "Recurring VPN rekey instability for client-a",
|
|
171
|
-
"summary": "Observed 12 self-resolving rekey failures over 7 days.",
|
|
172
|
-
"incident_type": "vpn_instability"
|
|
173
|
-
}
|
|
190
|
+
{"ok": true, "items": []}
|
|
174
191
|
```
|
|
192
|
+
If `db_path` is omitted, items are empty by design.
|
|
175
193
|
|
|
176
|
-
|
|
177
|
-
```json
|
|
178
|
-
{
|
|
179
|
-
"ok": true,
|
|
180
|
-
"incident_memory_id": "inc_100"
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## 7. Search Incident Memory
|
|
194
|
+
### `GET /candidates`
|
|
187
195
|
|
|
188
|
-
|
|
196
|
+
Query:
|
|
197
|
+
- `limit` (default: `5`)
|
|
198
|
+
- `candidate_type` (optional)
|
|
199
|
+
- `decision_band` (optional)
|
|
200
|
+
- `min_score_total` (optional)
|
|
201
|
+
- `db_path` (optional)
|
|
189
202
|
|
|
190
|
-
|
|
203
|
+
Returns:
|
|
191
204
|
```json
|
|
192
|
-
{
|
|
193
|
-
"tenant_id": "client-a",
|
|
194
|
-
"query": "vpn flaps",
|
|
195
|
-
"limit": 10
|
|
196
|
-
}
|
|
205
|
+
{"ok": true, "count": 0, "items": []}
|
|
197
206
|
```
|
|
198
207
|
|
|
199
|
-
|
|
200
|
-
```json
|
|
201
|
-
{
|
|
202
|
-
"ok": true,
|
|
203
|
-
"results": [ ... ]
|
|
204
|
-
}
|
|
205
|
-
```
|
|
208
|
+
### `GET /signatures`
|
|
206
209
|
|
|
207
|
-
|
|
210
|
+
Query:
|
|
211
|
+
- `limit` (default: `5`)
|
|
212
|
+
- `event_family` (optional)
|
|
213
|
+
- `service` (optional)
|
|
214
|
+
- `min_occurrence_count` (optional)
|
|
215
|
+
- `db_path` (optional)
|
|
208
216
|
|
|
209
|
-
|
|
217
|
+
Returns:
|
|
218
|
+
```json
|
|
219
|
+
{"ok": true, "count": 0, "items": []}
|
|
220
|
+
```
|
|
210
221
|
|
|
211
|
-
### `
|
|
222
|
+
### `GET /canonical_events`
|
|
212
223
|
|
|
213
|
-
|
|
224
|
+
Query:
|
|
225
|
+
- `limit` (default: `20`)
|
|
226
|
+
- `tenant_id` (optional)
|
|
227
|
+
- `source` (optional)
|
|
228
|
+
- `host` (optional)
|
|
229
|
+
- `service` (optional)
|
|
230
|
+
- `severity` (optional)
|
|
231
|
+
- `db_path` (optional)
|
|
214
232
|
|
|
215
|
-
|
|
233
|
+
Returns:
|
|
216
234
|
```json
|
|
217
|
-
{
|
|
218
|
-
"subject_type": "signature",
|
|
219
|
-
"subject_id": "sig_456",
|
|
220
|
-
"limit": 10
|
|
221
|
-
}
|
|
235
|
+
{"ok": true, "count": 0, "items": []}
|
|
222
236
|
```
|
|
223
237
|
|
|
224
|
-
|
|
238
|
+
### `GET /stats`
|
|
239
|
+
|
|
240
|
+
Query:
|
|
241
|
+
- `db_path` (optional)
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
225
244
|
```json
|
|
226
245
|
{
|
|
227
246
|
"ok": true,
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
"
|
|
247
|
+
"received": 10,
|
|
248
|
+
"canonicalized": 9,
|
|
249
|
+
"parse_failed": 1,
|
|
250
|
+
"unsupported": 0,
|
|
251
|
+
"candidates_generated": 4,
|
|
252
|
+
"source_summaries": {
|
|
253
|
+
"source_type": [{"value":"syslog","count":9}],
|
|
254
|
+
"...": []
|
|
255
|
+
}
|
|
231
256
|
}
|
|
232
257
|
```
|
|
233
258
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
## 9. Daily Digest
|
|
259
|
+
### `GET /failures`
|
|
237
260
|
|
|
238
|
-
|
|
261
|
+
Query:
|
|
262
|
+
- `limit` (default: `20`)
|
|
263
|
+
- `status` (optional; one of received/canonicalized/parse_failed/unsupported)
|
|
264
|
+
- `db_path` (optional)
|
|
239
265
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
Request:
|
|
266
|
+
Returns:
|
|
243
267
|
```json
|
|
244
|
-
{
|
|
245
|
-
"tenant_id": "client-a",
|
|
246
|
-
"since": "2026-03-21T00:00:00Z",
|
|
247
|
-
"limit": 10
|
|
248
|
-
}
|
|
268
|
+
{"ok": true, "items": [], "count": 0, "status": null}
|
|
249
269
|
```
|
|
250
270
|
|
|
251
|
-
|
|
252
|
-
```json
|
|
253
|
-
{
|
|
254
|
-
"ok": true,
|
|
255
|
-
"items": [
|
|
256
|
-
{
|
|
257
|
-
"candidate_id": "cand_789",
|
|
258
|
-
"title": "Recurring VPN rekey instability",
|
|
259
|
-
"decision_band": "review",
|
|
260
|
-
"why_it_matters": "Repeated 12 times this week, self-resolved each time, and matches a prior incident.",
|
|
261
|
-
"score_total": 0.84
|
|
262
|
-
}
|
|
263
|
-
]
|
|
264
|
-
}
|
|
265
|
-
```
|
|
271
|
+
### `GET /ingest/recent`
|
|
266
272
|
|
|
267
|
-
|
|
273
|
+
Query:
|
|
274
|
+
- `limit` (default: `20`)
|
|
275
|
+
- `status` (optional filter)
|
|
276
|
+
- `db_path` (optional)
|
|
268
277
|
|
|
269
|
-
|
|
278
|
+
Returns latest raw envelopes ordered newest-first.
|
|
270
279
|
|
|
271
|
-
### `
|
|
280
|
+
### `GET /failures/{raw_envelope_id}`
|
|
272
281
|
|
|
273
|
-
|
|
282
|
+
Returns one raw envelope row or 404 if missing.
|
|
274
283
|
|
|
275
|
-
|
|
276
|
-
```json
|
|
277
|
-
{
|
|
278
|
-
"tenant_id": "client-a",
|
|
279
|
-
"subject_type": "candidate",
|
|
280
|
-
"subject_id": "cand_789",
|
|
281
|
-
"decision": "useful",
|
|
282
|
-
"notes": "This pattern caused tickets last month.",
|
|
283
|
-
"reviewer": "steven"
|
|
284
|
-
}
|
|
285
|
-
```
|
|
284
|
+
### `GET /raw_envelopes`
|
|
286
285
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
286
|
+
Query:
|
|
287
|
+
- `limit` (default: `20`)
|
|
288
|
+
- `status` (optional; one of received/canonicalized/parse_failed/unsupported)
|
|
289
|
+
- `tenant_id` (optional)
|
|
290
|
+
- `source_type` (optional)
|
|
291
|
+
- `source_id` (optional)
|
|
292
|
+
- `source_path` (optional)
|
|
293
|
+
- `db_path` (optional)
|
|
294
294
|
|
|
295
|
-
|
|
295
|
+
Returns matching raw envelopes ordered newest-first with compact inspection fields (tenant/source metadata, message, status, parsing metadata).
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
### `GET /sources`
|
|
298
298
|
|
|
299
|
-
|
|
299
|
+
Query:
|
|
300
|
+
- `limit` (default: `10`)
|
|
301
|
+
- `db_path` (optional)
|
|
300
302
|
|
|
301
|
-
|
|
303
|
+
Returns per-dimension counts for source metadata.
|
|
302
304
|
|
|
303
|
-
|
|
304
|
-
```json
|
|
305
|
-
{
|
|
306
|
-
"tenant_id": "client-a",
|
|
307
|
-
"resource_id": 12345,
|
|
308
|
-
"host": "edge-fw-01",
|
|
309
|
-
"service": "vpn",
|
|
310
|
-
"severity": "warning",
|
|
311
|
-
"alert_id": 998877,
|
|
312
|
-
"message_raw": "VPN tunnel dropped and recovered",
|
|
313
|
-
"timestamp": "2026-03-22T00:00:00Z",
|
|
314
|
-
"metadata": {
|
|
315
|
-
"datasource": "IPSec Tunnel",
|
|
316
|
-
"instance_name": "site-b",
|
|
317
|
-
"acknowledged": false,
|
|
318
|
-
"cleared_at": "2026-03-22T00:00:32Z"
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
305
|
+
### `GET /sources/status`
|
|
322
306
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
```
|
|
307
|
+
Query:
|
|
308
|
+
- `limit` (default: `20`)
|
|
309
|
+
- `tenant_id` (optional filter)
|
|
310
|
+
- `source_type` (optional filter)
|
|
311
|
+
- `source_id` (optional filter)
|
|
312
|
+
- `source_path` (optional filter)
|
|
313
|
+
- `db_path` (optional)
|
|
332
314
|
|
|
333
|
-
|
|
315
|
+
Returns one row per source dimension with raw/failed/canonicalized counts.
|
|
334
316
|
|
|
335
|
-
|
|
317
|
+
### `GET /healthz`
|
|
336
318
|
|
|
337
|
-
|
|
338
|
-
```json
|
|
339
|
-
{
|
|
340
|
-
"tenant_id": "client-a",
|
|
341
|
-
"since": "2026-03-21T00:00:00Z",
|
|
342
|
-
"mode": "alerts"
|
|
343
|
-
}
|
|
344
|
-
```
|
|
319
|
+
Always available; useful for liveness.
|
|
345
320
|
|
|
346
|
-
Response:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"ingested": 250,
|
|
351
|
-
"signatures_created": 9,
|
|
352
|
-
"candidates_created": 4
|
|
353
|
-
}
|
|
354
|
-
```
|
|
321
|
+
Response is identical to `GET /status` and includes:
|
|
322
|
+
- `ok`
|
|
323
|
+
- `status`
|
|
324
|
+
- runtime summary including auth/capability/config state
|
|
355
325
|
|
|
356
|
-
|
|
326
|
+
### `GET /status`
|
|
357
327
|
|
|
358
|
-
|
|
359
|
-
|
|
328
|
+
Operator-oriented runtime summary with the same payload as `/healthz`.
|
|
329
|
+
- `ok`
|
|
330
|
+
- `status`
|
|
331
|
+
- `runtime`: version, auth_state, defaults, limits, capabilities
|
|
332
|
+
- `api_token_enabled`
|
|
360
333
|
|
|
361
|
-
### `GET /
|
|
362
|
-
|
|
334
|
+
### `GET /runtime`
|
|
335
|
+
|
|
336
|
+
Returns canonical runtime snapshot:
|
|
337
|
+
- `version`
|
|
338
|
+
- `api_token_env`
|
|
339
|
+
- `capability_flags.source_capabilities` (`source_types` + per-source ingest-mode matrix)
|
|
340
|
+
- runtime defaults and limits
|
|
341
|
+
- listener defaults
|
|
342
|
+
- endpoint capability flags
|
|
343
|
+
|
|
344
|
+
Current `capability_flags.source_capabilities` includes:
|
|
345
|
+
- `source_types`: `file`, `logicmonitor`, `syslog`
|
|
346
|
+
- `ingest_modes_by_source_type`:
|
|
347
|
+
- `syslog`: `single_event_api`, `batch_api`, `udp_listener`
|
|
348
|
+
- `file`: `single_event_api`, `batch_api`
|
|
349
|
+
- `logicmonitor`: `logicmonitor_webhook`
|
|
363
350
|
|
|
364
|
-
|
|
365
|
-
Bounded metrics and counts. Must not block on deep correlation or full integrity scans.
|
|
351
|
+
`GET /runtime` is a fuller diagnostic snapshot with the same runtime summary object used by `/status`/`/healthz`.
|
|
366
352
|
|
|
367
|
-
|
|
353
|
+
## Listener + file/syslog foundation alignment
|
|
368
354
|
|
|
369
|
-
|
|
355
|
+
The runtime path is deliberately split:
|
|
356
|
+
- API path (`/ingest/*`) for HTTP ingestion, replay, and inspection
|
|
357
|
+
- UDP syslog path (`brainstem.listener`) for direct syslog intake and optional stdout event emission
|
|
358
|
+
- file intake for helper ingestion flow through `source_type: file` payloads
|
|
370
359
|
|
|
371
|
-
For
|
|
372
|
-
- `/events/ingest`
|
|
373
|
-
- `/events/ingest_bulk`
|
|
374
|
-
- `/candidates/search`
|
|
375
|
-
- `/candidates/explain`
|
|
376
|
-
- `/candidates/promote`
|
|
377
|
-
- `/digest/daily`
|
|
378
|
-
- `/history/related`
|
|
379
|
-
- `/healthz`
|
|
380
|
-
- `/metrics`
|
|
360
|
+
For listener operator notes and full step-by-step startup, see [docs/README.md](README.md).
|
package/package.json
CHANGED