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