@uptimizr/collector-server 2.0.0 → 2.0.1
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/AGENTS.md +193 -0
- package/llms.txt +38 -0
- package/package.json +8 -6
package/AGENTS.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# AGENTS.md — @uptimizr/collector-server
|
|
2
|
+
|
|
3
|
+
> Packaged agent guide. For the human reference see [README.md](./README.md); for design
|
|
4
|
+
> rationale see the project ADRs at https://github.com/RaananW/Uptimizr/tree/main/docs/adr.
|
|
5
|
+
|
|
6
|
+
## What this package is
|
|
7
|
+
|
|
8
|
+
The public-facing **ingestion + query API** (Fastify) for the OSS data collector, plus the
|
|
9
|
+
`uptimizr` CLI that self-hosts it. All client input is untrusted and validated against
|
|
10
|
+
`@uptimizr/schema` at the boundary. Route handlers stay thin; storage lives behind a swappable
|
|
11
|
+
`CollectorStore` (ADR 0005).
|
|
12
|
+
|
|
13
|
+
It is the **single gateway** to the data: the dashboard, `@uptimizr/mcp`, the in-browser assistant
|
|
14
|
+
and your own agents all read through this HTTP API, never the database.
|
|
15
|
+
|
|
16
|
+
## Run it
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 1. One-time setup: generate a visitor-hash secret, create + migrate the store,
|
|
20
|
+
# mint a first project + API key, write a local .env.
|
|
21
|
+
npx -p @uptimizr/collector-server uptimizr init "My Project"
|
|
22
|
+
|
|
23
|
+
# 2. Start the ingestion + query API (reads the generated .env; 0.0.0.0:4318).
|
|
24
|
+
npx -p @uptimizr/collector-server uptimizr serve
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`init` prints a **`projectId`** (public — give it to your client SDK along with this server's URL)
|
|
28
|
+
and a one-time **API key** (secret — `x-api-key` for the query routes).
|
|
29
|
+
|
|
30
|
+
### CLI (ADR 0029)
|
|
31
|
+
|
|
32
|
+
| Command | What it does |
|
|
33
|
+
| ------------------------------ | -------------------------------------------------------------------- |
|
|
34
|
+
| `uptimizr init [name]` | Secret + store + migrations + first project/key + `.env`. |
|
|
35
|
+
| `uptimizr serve` | Run the ingestion + query API. The default when no command is given. |
|
|
36
|
+
| `uptimizr new-project <name>` | Mint an additional project + API key. |
|
|
37
|
+
| `uptimizr new-key <projectId>` | Mint an additional key on an existing project (see the flags below). |
|
|
38
|
+
| `uptimizr migrate` | Apply store migrations. |
|
|
39
|
+
| `uptimizr regions set <scene>` | Replace a scene's named regions from `--file <regions.json>`. |
|
|
40
|
+
| `uptimizr regions get <scene>` | Print a scene's named regions as JSON. |
|
|
41
|
+
| `uptimizr help` | Usage. |
|
|
42
|
+
|
|
43
|
+
`new-key` flags: `--capabilities <list>` (comma-separated; default `query`), `--label <name>`,
|
|
44
|
+
and the paired `--rate-limit-max <n>` / `--rate-limit-window-ms <ms>`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uptimizr new-key <projectId> --capabilities query,annotate \
|
|
48
|
+
--label "weekly-report-agent" --rate-limit-max 120 --rate-limit-window-ms 60000
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`regions` commands take `--project <projectId>` (or `UPTIMIZR_PROJECT_ID`). They talk to the store
|
|
52
|
+
**directly**, so they need no API key — an operator command, unlike the HTTP equivalent.
|
|
53
|
+
|
|
54
|
+
**Every command targets the store selected by `COLLECTOR_STORE`**, read through the same
|
|
55
|
+
connection variables `serve` uses — so export the store + its settings before `init` and the
|
|
56
|
+
project you mint is the one the running collector resolves.
|
|
57
|
+
|
|
58
|
+
Installed as a dependency, the package exposes the `uptimizr` CLI plus the legacy
|
|
59
|
+
`uptimizr-collector` bin (equivalent to `uptimizr serve`).
|
|
60
|
+
|
|
61
|
+
## API keys and capabilities (ADR 0051 §7)
|
|
62
|
+
|
|
63
|
+
A key carries a **set of capabilities**, not a single role. Keys default to `query`.
|
|
64
|
+
|
|
65
|
+
| Capability | Unlocks |
|
|
66
|
+
| ----------- | --------------------------------------------------------------------------------------------------------- |
|
|
67
|
+
| `query` | The aggregate analytics API, the scene registry, the live token exchange, and `GET /api/v1/audit`. |
|
|
68
|
+
| `query:raw` | Raw per-session streams: `GET /api/v1/sessions/:id/events` and `GET /api/v1/live/sessions/:id`. |
|
|
69
|
+
| `annotate` | The project **metadata** write path (annotations, glossary, saved analyses, panel specs). Never events. |
|
|
70
|
+
| `ingest` | Reserved for server-side write paths. Public ingestion is keyless, so issued keys are normally read keys. |
|
|
71
|
+
|
|
72
|
+
The raw endpoints are gated **twice**: the collector must run with `ENABLE_RAW_SESSION_RETENTION`
|
|
73
|
+
**and** the key must hold `query:raw` — otherwise `403`. Retention alone is not enough.
|
|
74
|
+
|
|
75
|
+
`--rate-limit-max` / `--rate-limit-window-ms` give a key its own request budget, bucketed on the
|
|
76
|
+
**key id** rather than the client IP; keys without one fall back to `COLLECTOR_RATE_LIMIT_*`.
|
|
77
|
+
Ingestion keeps its separate `COLLECTOR_INGEST_RATE_LIMIT_*` budget.
|
|
78
|
+
|
|
79
|
+
## Endpoints an agent should know
|
|
80
|
+
|
|
81
|
+
- **`GET /api/v1/openapi.json`** — **unauthenticated** OpenAPI 3.1 for the whole read API,
|
|
82
|
+
generated from the `@uptimizr/metrics` registry (ADR 0051) and this server's route table. One
|
|
83
|
+
path per endpoint, the real validating schema per parameter, a response schema per metric, and
|
|
84
|
+
the semantics OpenAPI cannot express as `x-uptimizr-*` extensions (result `grain`, per-column
|
|
85
|
+
`units`, `caveats`, `interpretation`, source capture channels, row `limits`). **Start here**
|
|
86
|
+
rather than guessing routes; generate a typed client with
|
|
87
|
+
`npx openapi-typescript <collector>/api/v1/openapi.json -o collector.d.ts`.
|
|
88
|
+
- **`GET /api/v1/whoami`** — the calling key's `projectId`, `keyId`, `capabilities`, `label` and
|
|
89
|
+
effective `rateLimit` (plus `rateLimitSource`: `"key"` or `"default"`). It reports the key's
|
|
90
|
+
**id**, never the key. Call it first and register only the tools your capabilities permit,
|
|
91
|
+
instead of probing for `403`s.
|
|
92
|
+
- **`GET /api/v1/audit`** (`since` / `until` / `limit`, any `query` key) — the agent audit log.
|
|
93
|
+
- `POST /api/v1/collect` — batched ingestion. **Keyless by design** (it runs in untrusted
|
|
94
|
+
browsers, where a key is not a secret); protected by schema validation, payload bounds and rate
|
|
95
|
+
limits instead. The server sets the cookieless `visitorId = hash(ip + ua + dailySalt)`; the raw
|
|
96
|
+
IP is never stored (ADR 0003).
|
|
97
|
+
- The read API: sessions, heatmaps (`pointer`, `camera`, `position`, `world`, `gaze`, `mesh-uv`,
|
|
98
|
+
`click-rays`, `flow`, `perf`, `errors`), mesh/interaction insights, performance and diagnostics,
|
|
99
|
+
scene/path/funnel analytics, scene representations and regions, and the live SSE endpoints.
|
|
100
|
+
- `GET /health` — liveness probe, unauthenticated.
|
|
101
|
+
|
|
102
|
+
### Result envelopes: `format=full | table | summary` (ADR 0051 §2)
|
|
103
|
+
|
|
104
|
+
Every aggregate endpoint accepts `format`. It **filters nothing** — it picks the result envelope:
|
|
105
|
+
|
|
106
|
+
- `full` (default) — the bare rows, unchanged. What the dashboard uses.
|
|
107
|
+
- `table` — adds a `meta` envelope: metric, range, applied filters, sample size, row count,
|
|
108
|
+
`truncated`, limits.
|
|
109
|
+
- `summary` — a bounded digest: ranked top rows, a first/last/min/max/trend series, or merged
|
|
110
|
+
spatial clusters, with shares, the metric's caveats and a templated `reading` sentence, capped at
|
|
111
|
+
the registry's `maxSummaryRows`. **This is what makes a 500-bin heatmap affordable for an LLM** —
|
|
112
|
+
prefer it over `full` when feeding a model.
|
|
113
|
+
|
|
114
|
+
### Filters
|
|
115
|
+
|
|
116
|
+
Common query params: `since`, `until` (epoch ms), `bins`, `limit`, `scene`, `session`,
|
|
117
|
+
`cameraMode`, `source`, and spatial `cellSize` / **`region`** where supported. `region=<id>` drills
|
|
118
|
+
a spatial query into one named place from the scene registry — declare regions with
|
|
119
|
+
`uptimizr regions set`, `PUT /api/v1/scenes/:sceneId/regions` (an `annotate` key), or
|
|
120
|
+
`registerRegions` in `@uptimizr/sdk-core`.
|
|
121
|
+
|
|
122
|
+
### Live endpoints
|
|
123
|
+
|
|
124
|
+
`POST /api/v1/live/token` exchanges a query key for a short-lived SSE token; the key's capability
|
|
125
|
+
set is carried **inside the signed token**, so `GET /api/v1/live/sessions/:id` can enforce
|
|
126
|
+
`query:raw` even though `EventSource` cannot send headers. `GET /api/v1/live/presence` and
|
|
127
|
+
`/live/stream` use the same `?token=...`.
|
|
128
|
+
|
|
129
|
+
## Storage (`COLLECTOR_STORE`)
|
|
130
|
+
|
|
131
|
+
| Value | Store |
|
|
132
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `duckdb` **(default)** | Single-file OSS store: events **and** metadata in one file at `DUCKDB_PATH` (default `./data/uptimizr.duckdb`). No external service. **Single-writer** — one collector process per file; back up by copying the file. |
|
|
134
|
+
| `memory` | Dependency-free in-memory store for local dev / E2E only (`COLLECTOR_MEMORY_PROJECT_ID` / `COLLECTOR_MEMORY_API_KEY`). |
|
|
135
|
+
| `postgres` | `@uptimizr/db-postgres` — `POSTGRES_URL` / `DATABASE_URL`, `POSTGRES_SCHEMA`, `POSTGRES_POOL_MAX`. Multi-writer. |
|
|
136
|
+
| `mssql` | `@uptimizr/db-mssql` — `MSSQL_URL` (or `MSSQL_SERVER` / `MSSQL_PORT` / `MSSQL_DATABASE` / `MSSQL_USER` / `MSSQL_PASSWORD`). SQL Server 2022+ / Azure SQL. Multi-writer. |
|
|
137
|
+
| `clickhouse` | `@uptimizr/db-clickhouse` — `CLICKHOUSE_URL` / `CLICKHOUSE_DATABASE` / `CLICKHOUSE_USER` / `CLICKHOUSE_PASSWORD`. Concurrent writers, high-volume ingestion. |
|
|
138
|
+
|
|
139
|
+
All four return **identical analytics** (the cross-engine parity suite). Aggregations are computed
|
|
140
|
+
at **query time** in v1 — no materialized views.
|
|
141
|
+
|
|
142
|
+
## Other configuration
|
|
143
|
+
|
|
144
|
+
- Server / browser access: `COLLECTOR_HOST` (`0.0.0.0`), `COLLECTOR_PORT` (`4318`),
|
|
145
|
+
`COLLECTOR_CORS_ORIGINS`, `COLLECTOR_TRUST_PROXY`, `COLLECTOR_BODY_LIMIT`.
|
|
146
|
+
- Privacy / replay / live: **`VISITOR_HASH_SECRET` (required — the server fails fast without it)**,
|
|
147
|
+
`ENABLE_RAW_SESSION_RETENTION`, `LIVE_TOKEN_SECRET`, `LIVE_TOKEN_TTL_MS`, `LIVE_WINDOW_MS`,
|
|
148
|
+
`LIVE_MAX_CONNECTIONS`, `LIVE_PRESENCE_INTERVAL_MS`.
|
|
149
|
+
- Rate limits: `COLLECTOR_RATE_LIMIT_MAX`, `COLLECTOR_RATE_LIMIT_WINDOW_MS`,
|
|
150
|
+
`COLLECTOR_INGEST_RATE_LIMIT_MAX`, `COLLECTOR_INGEST_RATE_LIMIT_WINDOW_MS`.
|
|
151
|
+
- Agent audit: **`AUDIT_RETENTION_DAYS`** (default `30`; `0` = keep forever),
|
|
152
|
+
`AUDIT_DASHBOARD_REQUESTS` (default off — requests carrying `x-uptimizr-client: dashboard` are
|
|
153
|
+
skipped as a volume filter, **not** a security boundary).
|
|
154
|
+
- All-in-one dashboard: `COLLECTOR_DASHBOARD_DIR` (point it at a static dashboard export and one
|
|
155
|
+
process serves ingestion, queries and the UI), `COLLECTOR_CSP` (`strict` or `off`).
|
|
156
|
+
|
|
157
|
+
## Rules for agents
|
|
158
|
+
|
|
159
|
+
- **Validate at the boundary.** Every request body and querystring is parsed against
|
|
160
|
+
`@uptimizr/schema` / the metric registry. Never trust client input, never skip validation.
|
|
161
|
+
- **Keep handlers thin** (ADR 0005). Aggregation logic belongs in `@uptimizr/db`; the semantics of
|
|
162
|
+
a metric belong in `@uptimizr/metrics`. A new read endpoint is a **registry entry**, not a
|
|
163
|
+
hand-written route — `registryRoutes.test.ts` asserts the endpoint exists and that its
|
|
164
|
+
querystring keys equal the metric's `filters`, and `queryResponseSchemas.test.ts` asserts every
|
|
165
|
+
endpoint's rows parse against the metric's `row` schema on a seeded store, an empty one, and the
|
|
166
|
+
in-memory store.
|
|
167
|
+
- **Never weaken the raw-data gate.** `ENABLE_RAW_SESSION_RETENTION` **and** `query:raw`, both,
|
|
168
|
+
for `/sessions/:id/events` and `/live/sessions/:id` (ADR 0003).
|
|
169
|
+
- **Never log secrets or raw IPs.** The audit log's subject is the key **id**; `params` drops
|
|
170
|
+
credential-shaped fields and is capped at 512 bytes. Audit writes happen after the response is
|
|
171
|
+
flushed, so they can never block or fail a request. Refusals are recorded too.
|
|
172
|
+
- **Ingestion stays keyless.** Do not "fix" it by requiring a key — a key shipped to an untrusted
|
|
173
|
+
browser is not a secret. Harden with validation, payload bounds, rate limits and CORS instead.
|
|
174
|
+
- Events live once in `@uptimizr/schema`. Do not redefine an event shape here.
|
|
175
|
+
- Aggregations are query-time in v1; do not add materialized views without an ADR.
|
|
176
|
+
|
|
177
|
+
## Develop
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pnpm --filter @uptimizr/collector-server dev # tsx watch
|
|
181
|
+
pnpm --filter @uptimizr/collector-server test # vitest (app.inject() + a fake store)
|
|
182
|
+
pnpm --filter @uptimizr/collector-server build
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Tests run against a fake `CollectorStore` with `app.inject()` — no live database required.
|
|
186
|
+
|
|
187
|
+
## More
|
|
188
|
+
|
|
189
|
+
- Package reference: [README.md](./README.md)
|
|
190
|
+
- HTTP API reference:
|
|
191
|
+
https://github.com/RaananW/Uptimizr/blob/main/docs/integration.md#4-http-api
|
|
192
|
+
- Query API guide: https://uptimizr.com/docs/api/query/
|
|
193
|
+
- Deploy guide: https://uptimizr.com/docs/deploy/collector/
|
package/llms.txt
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @uptimizr/collector-server
|
|
2
|
+
|
|
3
|
+
> The OSS ingestion + query API (Fastify) for Uptimizr, plus the `uptimizr` CLI that self-hosts
|
|
4
|
+
> it. Keyless, schema-validated ingestion; `x-api-key` read routes scoped to the key's project and
|
|
5
|
+
> capability set. The single gateway to the data — the dashboard, `@uptimizr/mcp` and your own
|
|
6
|
+
> agents all read through this HTTP API, never the database. Storage is swappable
|
|
7
|
+
> (`COLLECTOR_STORE`: DuckDB by default, plus Postgres / SQL Server / ClickHouse).
|
|
8
|
+
|
|
9
|
+
## Docs
|
|
10
|
+
|
|
11
|
+
- [Package reference](./README.md): self-host, all endpoints, security, the full configuration list.
|
|
12
|
+
- [Agent guide](./AGENTS.md): the CLI, capabilities, result envelopes, filters, stores, and the rules an agent must follow.
|
|
13
|
+
- [HTTP API reference](https://github.com/RaananW/Uptimizr/blob/main/docs/integration.md#4-http-api): keys, capabilities, rate limits, the audit log, storage backends.
|
|
14
|
+
- [Query API guide](https://uptimizr.com/docs/api/query/): every read endpoint and the result formats.
|
|
15
|
+
- [Deploy guide](https://uptimizr.com/docs/deploy/collector/): running it in production.
|
|
16
|
+
- [Architecture Decision Records](https://github.com/RaananW/Uptimizr/tree/main/docs/adr): privacy model (0003), thin backends (0005), migrations (0007), consumer-facing agents (0017), open-core storage boundary (0020), distribution & self-host DX (0029), live sessions (0032), AI-first analytics layer (0051).
|
|
17
|
+
|
|
18
|
+
## Self-describe before you call
|
|
19
|
+
|
|
20
|
+
- `GET /api/v1/openapi.json` — unauthenticated OpenAPI 3.1 for the whole read API, generated from
|
|
21
|
+
the `@uptimizr/metrics` registry: one path per endpoint, real parameter schemas, a response
|
|
22
|
+
schema per metric, plus `x-uptimizr-*` extensions carrying grain, column units, caveats,
|
|
23
|
+
interpretation, source capture channels and row limits.
|
|
24
|
+
- `GET /api/v1/whoami` — the calling key's project, key id, capabilities, label and effective rate
|
|
25
|
+
limit.
|
|
26
|
+
- `GET /api/v1/audit?since=&until=&limit=` — the agent audit log (key ids, never keys).
|
|
27
|
+
|
|
28
|
+
## Key facts
|
|
29
|
+
|
|
30
|
+
- **CLI:** `uptimizr init | serve | new-project <name> | new-key <projectId> | migrate | regions set|get <sceneId> | help`. `new-key` takes `--capabilities`, `--label`, `--rate-limit-max`, `--rate-limit-window-ms`. Every command targets `COLLECTOR_STORE`.
|
|
31
|
+
- **Capabilities:** `ingest` (server-side writes), `query` (aggregates, scene registry, live token, audit), `annotate` (project metadata writes), `query:raw` (raw per-session + live-follow streams). Default `query`.
|
|
32
|
+
- **Raw data is gated twice:** `ENABLE_RAW_SESSION_RETENTION` **and** a `query:raw` key, or `403`.
|
|
33
|
+
- **Result envelopes:** `format=full | table | summary` on every aggregate endpoint (ADR 0051 §2) — `summary` is the bounded, LLM-affordable digest.
|
|
34
|
+
- **Filters:** `since`, `until`, `bins`, `limit`, `scene`, `session`, `cameraMode`, `source`, `cellSize`, and `region=<id>` to drill a spatial query into a named place.
|
|
35
|
+
- **Audit retention:** `AUDIT_RETENTION_DAYS` (default `30`, `0` = forever); `AUDIT_DASHBOARD_REQUESTS` to also record dashboard traffic.
|
|
36
|
+
- **Stores:** `COLLECTOR_STORE=duckdb` (default, single-file, single-writer, `DUCKDB_PATH`) | `memory` | `postgres` | `mssql` | `clickhouse` — identical analytics on all four (parity suite).
|
|
37
|
+
- **Required:** `VISITOR_HASH_SECRET`. The server fails fast without it; the raw IP is never stored.
|
|
38
|
+
- **Bins:** `uptimizr` (`dist/cli.js`) and the legacy `uptimizr-collector` (`dist/server.js`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uptimizr/collector-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Uptimizr ingestion + query API (Fastify). Self-hostable collector for the OSS data-collector.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"uptimizr",
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"files": [
|
|
36
36
|
"dist",
|
|
37
37
|
"README.md",
|
|
38
|
-
"LICENSE"
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"AGENTS.md",
|
|
40
|
+
"llms.txt"
|
|
39
41
|
],
|
|
40
42
|
"dependencies": {
|
|
41
43
|
"@fastify/cors": "^11.3.0",
|
|
@@ -45,12 +47,12 @@
|
|
|
45
47
|
"fastify": "^5.12.3",
|
|
46
48
|
"fastify-type-provider-zod": "^7.0.0",
|
|
47
49
|
"zod": "^4.5.4",
|
|
48
|
-
"@uptimizr/db-clickhouse": "2.0.
|
|
50
|
+
"@uptimizr/db-clickhouse": "2.0.1",
|
|
49
51
|
"@uptimizr/db": "2.0.0",
|
|
50
|
-
"@uptimizr/db-postgres": "2.0.
|
|
51
|
-
"@uptimizr/db-mssql": "2.0.0",
|
|
52
|
+
"@uptimizr/db-postgres": "2.0.1",
|
|
52
53
|
"@uptimizr/metrics": "0.1.0",
|
|
53
|
-
"@uptimizr/schema": "1.1.0"
|
|
54
|
+
"@uptimizr/schema": "1.1.0",
|
|
55
|
+
"@uptimizr/db-mssql": "2.0.1"
|
|
54
56
|
},
|
|
55
57
|
"devDependencies": {
|
|
56
58
|
"@seriousme/openapi-schema-validator": "^2.9.1",
|