@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.
Files changed (3) hide show
  1. package/AGENTS.md +193 -0
  2. package/llms.txt +38 -0
  3. 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.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.0",
50
+ "@uptimizr/db-clickhouse": "2.0.1",
49
51
  "@uptimizr/db": "2.0.0",
50
- "@uptimizr/db-postgres": "2.0.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",