@iann29/rastro 0.1.0-alpha.1 → 0.1.0-alpha.11
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/README.md +693 -69
- package/agent/integration.md +801 -0
- package/agent/manifest.json +205 -0
- package/agent/manifest.schema.json +444 -0
- package/dist/client/federation.d.ts +381 -0
- package/dist/client/federation.d.ts.map +1 -0
- package/dist/client/federation.js +274 -0
- package/dist/client/federation.js.map +1 -0
- package/dist/client/index.d.ts +2673 -15
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +565 -33
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +22 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +315 -4
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +4 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/affiliates.d.ts.map +1 -1
- package/dist/component/affiliates.js +6 -2
- package/dist/component/affiliates.js.map +1 -1
- package/dist/component/cardinality.d.ts +12 -0
- package/dist/component/cardinality.d.ts.map +1 -0
- package/dist/component/cardinality.js +94 -0
- package/dist/component/cardinality.js.map +1 -0
- package/dist/component/constants.d.ts +28 -1
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +44 -1
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +6 -1
- package/dist/component/convex.config.js +9 -1
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/coverage.d.ts +27 -0
- package/dist/component/coverage.d.ts.map +1 -0
- package/dist/component/coverage.js +56 -0
- package/dist/component/coverage.js.map +1 -0
- package/dist/component/diagnostics.d.ts +9 -0
- package/dist/component/diagnostics.d.ts.map +1 -0
- package/dist/component/diagnostics.js +47 -0
- package/dist/component/diagnostics.js.map +1 -0
- package/dist/component/errors.d.ts +1 -1
- package/dist/component/errors.d.ts.map +1 -1
- package/dist/component/errors.js.map +1 -1
- package/dist/component/eventStore.d.ts +21 -9
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/eventStore.js +142 -152
- package/dist/component/eventStore.js.map +1 -1
- package/dist/component/funnels.d.ts.map +1 -1
- package/dist/component/funnels.js +5 -3
- package/dist/component/funnels.js.map +1 -1
- package/dist/component/geo.d.ts +73 -0
- package/dist/component/geo.d.ts.map +1 -0
- package/dist/component/geo.js +648 -0
- package/dist/component/geo.js.map +1 -0
- package/dist/component/goals.d.ts.map +1 -1
- package/dist/component/goals.js +8 -5
- package/dist/component/goals.js.map +1 -1
- package/dist/component/guards.d.ts.map +1 -1
- package/dist/component/guards.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +281 -62
- package/dist/component/http.js.map +1 -1
- package/dist/component/identity.d.ts +13 -0
- package/dist/component/identity.d.ts.map +1 -0
- package/dist/component/identity.js +58 -0
- package/dist/component/identity.js.map +1 -0
- package/dist/component/ingest.d.ts +3 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +498 -95
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/live.d.ts.map +1 -1
- package/dist/component/live.js +7 -6
- package/dist/component/live.js.map +1 -1
- package/dist/component/localTime.d.ts +25 -0
- package/dist/component/localTime.d.ts.map +1 -0
- package/dist/component/localTime.js +126 -0
- package/dist/component/localTime.js.map +1 -0
- package/dist/component/reports.d.ts +266 -10
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +1243 -137
- package/dist/component/reports.js.map +1 -1
- package/dist/component/retention.d.ts +75 -1
- package/dist/component/retention.d.ts.map +1 -1
- package/dist/component/retention.js +561 -38
- package/dist/component/retention.js.map +1 -1
- package/dist/component/rollupStore.d.ts +320 -0
- package/dist/component/rollupStore.d.ts.map +1 -0
- package/dist/component/rollupStore.js +596 -0
- package/dist/component/rollupStore.js.map +1 -0
- package/dist/component/rollups.d.ts +20 -0
- package/dist/component/rollups.d.ts.map +1 -0
- package/dist/component/rollups.js +73 -0
- package/dist/component/rollups.js.map +1 -0
- package/dist/component/sanitize.d.ts +24 -1
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +96 -16
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +687 -65
- package/dist/component/schema.js +187 -20
- package/dist/component/schema.js.map +1 -1
- package/dist/component/sites.d.ts +12 -0
- package/dist/component/sites.d.ts.map +1 -1
- package/dist/component/sites.js +41 -7
- package/dist/component/sites.js.map +1 -1
- package/dist/component/useragent.d.ts +9 -0
- package/dist/component/useragent.d.ts.map +1 -0
- package/dist/component/useragent.js +152 -0
- package/dist/component/useragent.js.map +1 -0
- package/dist/component/validators.d.ts +124 -69
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +35 -14
- package/dist/component/validators.js.map +1 -1
- package/dist/component/visitors.d.ts +19 -0
- package/dist/component/visitors.d.ts.map +1 -0
- package/dist/component/visitors.js +86 -0
- package/dist/component/visitors.js.map +1 -0
- package/dist/component/vitals.d.ts +41 -0
- package/dist/component/vitals.d.ts.map +1 -0
- package/dist/component/vitals.js +115 -0
- package/dist/component/vitals.js.map +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/tracker/generated.d.ts +11 -4
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +11 -4
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +1 -1
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +57 -20
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker/vitals.d.ts +10 -0
- package/dist/tracker/vitals.d.ts.map +1 -0
- package/dist/tracker/vitals.js +140 -0
- package/dist/tracker/vitals.js.map +1 -0
- package/dist/tracker.min.js +1 -1
- package/dist/vitals.min.js +1 -0
- package/docs/benchmarks/2026-08-20-realistic.md +76 -76
- package/docs/benchmarks/2026-08-21-formal-certification.md +353 -0
- package/docs/benchmarks/2026-08-30-alpha6-recertification.md +206 -0
- package/docs/federation-setup.md +464 -0
- package/docs/federation.md +352 -0
- package/docs/upgrading.md +344 -0
- package/llms.txt +72 -0
- package/package.json +55 -11
- package/scripts/benchmark-ingest.mjs +175 -73
- package/scripts/generate-federation-keys.mjs +20 -0
- package/src/component/_generated/api.ts +22 -0
- package/src/component/_generated/component.ts +381 -4
- package/src/component/_generated/server.ts +4 -0
- package/src/component/affiliates.ts +20 -5
- package/src/component/cardinality.ts +117 -0
- package/src/component/constants.ts +44 -1
- package/src/component/convex.config.ts +11 -1
- package/src/component/coverage.ts +71 -0
- package/src/component/diagnostics.ts +65 -0
- package/src/component/errors.ts +2 -1
- package/src/component/eventStore.ts +217 -193
- package/src/component/funnels.ts +19 -16
- package/src/component/geo.ts +835 -0
- package/src/component/goals.ts +29 -21
- package/src/component/guards.ts +3 -1
- package/src/component/http.ts +404 -70
- package/src/component/identity.ts +74 -0
- package/src/component/ingest.ts +894 -188
- package/src/component/live.ts +13 -7
- package/src/component/localTime.ts +167 -0
- package/src/component/reports.ts +1872 -197
- package/src/component/retention.ts +788 -96
- package/src/component/rollupStore.ts +799 -0
- package/src/component/rollups.ts +82 -0
- package/src/component/sanitize.ts +144 -29
- package/src/component/schema.ts +217 -21
- package/src/component/sites.ts +59 -12
- package/src/component/useragent.ts +171 -0
- package/src/component/validators.ts +49 -14
- package/src/component/visitors.ts +116 -0
- package/src/component/vitals.ts +146 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Federated Amage Rastro dashboard
|
|
2
|
+
|
|
3
|
+
This document is the protocol reference. Follow
|
|
4
|
+
[`federation-setup.md`](federation-setup.md) for the executable customer-host
|
|
5
|
+
installation, pairing, verification, and revocation sequence.
|
|
6
|
+
|
|
7
|
+
The federated dashboard model keeps telemetry in the customer's Convex or
|
|
8
|
+
Synapse deployment while a browser loaded from `amagerastro.com` subscribes
|
|
9
|
+
directly to authorized host functions in that deployment.
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
tracker -> customer deployment -> Rastro component tables
|
|
13
|
+
|
|
14
|
+
amagerastro.com browser -> customer deployment -> reactive Rastro reports
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The Amage Rastro control plane stores account and connection metadata. It does
|
|
18
|
+
not need to proxy or persist the customer's analytics events.
|
|
19
|
+
|
|
20
|
+
## Connector protocol v1
|
|
21
|
+
|
|
22
|
+
The typed `FEDERATED_ANALYTICS_CONNECTOR_MANIFEST` constant defines the stable
|
|
23
|
+
v1 contract:
|
|
24
|
+
|
|
25
|
+
- protocol: `amage-rastro-analytics`
|
|
26
|
+
- protocol version: `1`
|
|
27
|
+
- canonical host module: `rastroFederation`
|
|
28
|
+
- canonical function names and supported capabilities
|
|
29
|
+
- public request, connection, range, live visitor, and journey limits
|
|
30
|
+
|
|
31
|
+
The host module must be `convex/rastroFederation.ts`, so its public references
|
|
32
|
+
match the function names advertised by the manifest. The generated `manifest`
|
|
33
|
+
query is public because it contains only static protocol metadata. It does not
|
|
34
|
+
resolve a connection or expose customer data. Compare a served manifest with
|
|
35
|
+
`agent/manifest.json`'s `connector` object structurally, never as serialized
|
|
36
|
+
text: the two are the same value with different key order, and a byte-for-byte
|
|
37
|
+
comparison fails on a correct host.
|
|
38
|
+
|
|
39
|
+
The exact generated surface is:
|
|
40
|
+
|
|
41
|
+
- `manifest`
|
|
42
|
+
- `connectionStatus`
|
|
43
|
+
- `listSites`
|
|
44
|
+
- `overview`
|
|
45
|
+
- `liveVisitors`
|
|
46
|
+
- `listSessions`
|
|
47
|
+
- `sessionJourney`
|
|
48
|
+
- `listConversions`
|
|
49
|
+
- `visitorJourney`
|
|
50
|
+
- `goalsReport`
|
|
51
|
+
- `funnelsReport`
|
|
52
|
+
- `affiliatesReport`
|
|
53
|
+
- `dataCoverage`
|
|
54
|
+
- `vitalsReport` (optional capability `vitals`, hosts from `alpha.6`)
|
|
55
|
+
- `siteMap` (optional capability `siteMap`, hosts from `alpha.8`)
|
|
56
|
+
- the configure scope (optional capability `configure`, hosts from `alpha.11`):
|
|
57
|
+
`siteSettings`, `updateSite`, `listGoals`, `upsertGoal`, `removeGoal`,
|
|
58
|
+
`listFunnels`, `upsertFunnel`, `removeFunnel`, `listAffiliates`,
|
|
59
|
+
`upsertAffiliate`, `removeAffiliate`, `retentionStatus`, `setRetentionPolicy`,
|
|
60
|
+
and `disableRetentionPolicy`
|
|
61
|
+
|
|
62
|
+
Hosts from `alpha.11` also advertise the optional capability `localDays`: their
|
|
63
|
+
`overview` answers a daily range that covers whole calendar days in the site's
|
|
64
|
+
timezone from buckets kept in that zone, and `listSites` says per site which
|
|
65
|
+
zone that is and since when the days are complete. Dashboards must not require
|
|
66
|
+
the capability.
|
|
67
|
+
|
|
68
|
+
There are no ingestion, owner-wide enumeration, or arbitrary host functions in
|
|
69
|
+
this surface, and no write runs with a read-only token: every function of the
|
|
70
|
+
configure scope requires the `analytics:configure` permission on the token
|
|
71
|
+
**and** on the host's local grant, as described under
|
|
72
|
+
[Configure scope](#configure-scope). The read-only goal, funnel, and affiliate
|
|
73
|
+
reports include their bounded definitions, and `listConversions` returns only
|
|
74
|
+
the trusted server-side conversion ledger. Browser `conversion` telemetry
|
|
75
|
+
remains untrusted journey data and never enters that ledger. Journey events
|
|
76
|
+
include bounded custom properties. `exposeFederatedAnalyticsApi` accepts only
|
|
77
|
+
`FederatedAnalyticsAuthorizerOptions`; it does not accept or expose an arbitrary
|
|
78
|
+
host `AnalyticsAuthorizer`.
|
|
79
|
+
|
|
80
|
+
`connectionStatus` authenticates and resolves the authoritative connection on
|
|
81
|
+
every call. Its response is deliberately redacted to status, protocol version,
|
|
82
|
+
capabilities, the effective permissions, and site count. It does not return
|
|
83
|
+
connection IDs, organization IDs, identity claims, or site IDs.
|
|
84
|
+
|
|
85
|
+
`listSites` takes no arguments. It loads sites only from the `siteIds` in the
|
|
86
|
+
validated local connection and returns `FederatedSiteSummary` values containing
|
|
87
|
+
only `siteId`, `name`, `currency`, `timezone`, `cookieless`, and (hosts from
|
|
88
|
+
`alpha.11`) `localDays` — `{ timezone, since }` when the host keeps the site's
|
|
89
|
+
calendar days, `null` otherwise. In particular, it never returns `ownerId`,
|
|
90
|
+
`domains`, or `networkId`.
|
|
91
|
+
|
|
92
|
+
## Configure scope
|
|
93
|
+
|
|
94
|
+
Hosts from `alpha.11` advertise the optional `configure` capability. Behind it,
|
|
95
|
+
the dashboard's organization owners and admins can manage goals, funnels,
|
|
96
|
+
affiliates, site settings, and the retention policy of the granted sites without
|
|
97
|
+
a host deploy. The scope is off unless the host opts in:
|
|
98
|
+
|
|
99
|
+
- The control plane mints `rastro_permissions: ["analytics:read"]` for every
|
|
100
|
+
member and adds `"analytics:configure"` only for members whose organization
|
|
101
|
+
role is `owner` or `admin`.
|
|
102
|
+
- The host's local grant lists the permissions it honors. A grant without
|
|
103
|
+
`permissions`, or without `analytics:configure` in it, is read only whatever
|
|
104
|
+
the token claims.
|
|
105
|
+
- A configure function runs only when both agree; otherwise it fails with
|
|
106
|
+
`FEDERATION_CONFIGURE_FORBIDDEN` before the site scope is examined, so a
|
|
107
|
+
read-only token learns nothing about the grant by probing. `connectionStatus`
|
|
108
|
+
reports the effective permissions, and the dashboard renders forms only when
|
|
109
|
+
they include the configure scope; otherwise every form ends in the code the
|
|
110
|
+
host runs itself.
|
|
111
|
+
|
|
112
|
+
The scope is bounded to configuration: `upsertGoal`, `removeGoal`,
|
|
113
|
+
`upsertFunnel`, `removeFunnel`, `upsertAffiliate`, and `removeAffiliate` mirror
|
|
114
|
+
the `Rastro` class, `listGoals`, `listFunnels`, and `listAffiliates` return the
|
|
115
|
+
definitions, `setRetentionPolicy` and `disableRetentionPolicy` manage the
|
|
116
|
+
recurring cleanup that `retentionStatus` reports, and `updateSite` changes only
|
|
117
|
+
`name`, `domains`, and `timezone` — the currency is fixed by the ledger and
|
|
118
|
+
`cookieless`, `networkId`, and `ownerId` stay outside the protocol.
|
|
119
|
+
`siteSettings` is the one place the surface shows a site's `domains`, because a
|
|
120
|
+
connection allowed to change them must see them; `listSites` stays redacted for
|
|
121
|
+
every reader. Nothing in the scope ingests telemetry or enumerates sites beyond
|
|
122
|
+
the grant. The constants `FEDERATED_ANALYTICS_CONFIGURE_PERMISSION`,
|
|
123
|
+
`FEDERATED_ANALYTICS_PERMISSIONS`, `FEDERATED_ANALYTICS_CONFIGURE_FUNCTIONS`,
|
|
124
|
+
the `federatedAnalyticsPermissionValidator`, and `effectiveFederatedPermissions`
|
|
125
|
+
are exported for hosts and connector clients.
|
|
126
|
+
|
|
127
|
+
## Authentication contract
|
|
128
|
+
|
|
129
|
+
The customer host must configure Convex authentication to trust the Amage Rastro
|
|
130
|
+
OIDC issuer. Convex validates the JWT signature, issuer, audience, and expiry
|
|
131
|
+
before any host function runs. The helper then requires these custom claims:
|
|
132
|
+
|
|
133
|
+
| Claim | Value |
|
|
134
|
+
| ------------------------ | -------------------------------------------------------------------------- |
|
|
135
|
+
| `rastro_connection_id` | The paired deployment connection |
|
|
136
|
+
| `rastro_organization_id` | The Amage Rastro organization |
|
|
137
|
+
| `rastro_permissions` | Must include `analytics:read`; owners and admins add `analytics:configure` |
|
|
138
|
+
|
|
139
|
+
Claim names and the permission constants are exported as
|
|
140
|
+
`FEDERATED_ANALYTICS_CLAIMS`, `FEDERATED_ANALYTICS_READ_PERMISSION`, and
|
|
141
|
+
`FEDERATED_ANALYTICS_CONFIGURE_PERMISSION`.
|
|
142
|
+
|
|
143
|
+
The audience is the customer's normalized Convex deployment URL, for example
|
|
144
|
+
`https://product-123.convex.cloud`. It is stable across connections to that
|
|
145
|
+
deployment; `rastro_connection_id` identifies the individual grant. A customer
|
|
146
|
+
host configures the provider with the issuer and audience issued during pairing:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
// convex/auth.config.ts
|
|
150
|
+
import type { AuthConfig } from "convex/server";
|
|
151
|
+
|
|
152
|
+
export default {
|
|
153
|
+
providers: [
|
|
154
|
+
// Keep the application's existing providers here.
|
|
155
|
+
{
|
|
156
|
+
domain: process.env.RASTRO_FEDERATION_ISSUER!,
|
|
157
|
+
applicationID: process.env.RASTRO_FEDERATION_AUDIENCE!,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
} satisfies AuthConfig;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`RASTRO_FEDERATION_AUDIENCE` must exactly match the deployment URL registered in
|
|
164
|
+
the control plane. Insert the provider object into the host's existing
|
|
165
|
+
`providers` array; the complete insertion fragment is in
|
|
166
|
+
[`federation-setup.md`](federation-setup.md). Never replace existing providers.
|
|
167
|
+
The example control plane publishes OIDC discovery and JWKS endpoints under
|
|
168
|
+
`/federation`, signs RS256 tokens with a ten-minute lifetime, and never sends
|
|
169
|
+
its private key to the browser.
|
|
170
|
+
|
|
171
|
+
## Authoritative local connection
|
|
172
|
+
|
|
173
|
+
JWT claims identify a requested connection but do not define its site access.
|
|
174
|
+
The customer deployment resolves a local connection record on every
|
|
175
|
+
authenticated query. This record is authoritative and makes revocation immediate
|
|
176
|
+
instead of waiting for a token to expire.
|
|
177
|
+
|
|
178
|
+
A host schema can model it as:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
182
|
+
import { v } from "convex/values";
|
|
183
|
+
|
|
184
|
+
export default defineSchema({
|
|
185
|
+
rastroFederationGrants: defineTable({
|
|
186
|
+
connectionId: v.string(),
|
|
187
|
+
organizationId: v.string(),
|
|
188
|
+
siteIds: v.array(v.string()),
|
|
189
|
+
// Absent means read only; ["analytics:configure"] opts into the scope.
|
|
190
|
+
permissions: v.optional(v.array(v.string())),
|
|
191
|
+
createdAt: v.number(),
|
|
192
|
+
updatedAt: v.number(),
|
|
193
|
+
revokedAt: v.optional(v.number()),
|
|
194
|
+
}).index("by_connectionId", ["connectionId"]),
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Pairing and revocation mutations belong to the host application and must use its
|
|
199
|
+
existing administrator authorization. The Rastro component cannot inspect host
|
|
200
|
+
authentication state. The grant's `permissions`, like its `siteIds`, are the
|
|
201
|
+
host's decision: a token cannot grant itself the configure scope.
|
|
202
|
+
|
|
203
|
+
## Host API
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
// convex/rastroFederation.ts
|
|
207
|
+
import {
|
|
208
|
+
exposeFederatedAnalyticsApi,
|
|
209
|
+
type FederatedAnalyticsConnection,
|
|
210
|
+
type FederatedAnalyticsPermission,
|
|
211
|
+
} from "@iann29/rastro";
|
|
212
|
+
import { components } from "./_generated/api";
|
|
213
|
+
import { env, type QueryCtx } from "./_generated/server";
|
|
214
|
+
|
|
215
|
+
// RASTRO_FEDERATION_ISSUER is declared optional in convex.config.ts so a push
|
|
216
|
+
// never depends on it; the production issuer is the default.
|
|
217
|
+
const issuer = (
|
|
218
|
+
env.RASTRO_FEDERATION_ISSUER ?? "https://site.api.amagerastro.com/federation"
|
|
219
|
+
).replace(/\/$/, "");
|
|
220
|
+
|
|
221
|
+
const federated = exposeFederatedAnalyticsApi(components.rastroAnalytics, {
|
|
222
|
+
issuer,
|
|
223
|
+
resolveConnection: async (ctx, identity) => {
|
|
224
|
+
const db = ctx.db as unknown as QueryCtx["db"];
|
|
225
|
+
const grant = await db
|
|
226
|
+
.query("rastroFederationGrants")
|
|
227
|
+
.withIndex("by_connectionId", (query) =>
|
|
228
|
+
query.eq("connectionId", identity.connectionId),
|
|
229
|
+
)
|
|
230
|
+
.unique();
|
|
231
|
+
|
|
232
|
+
if (!grant || grant.organizationId !== identity.organizationId) return null;
|
|
233
|
+
return {
|
|
234
|
+
connectionId: grant.connectionId,
|
|
235
|
+
organizationId: grant.organizationId,
|
|
236
|
+
siteIds: grant.siteIds,
|
|
237
|
+
permissions: grant.permissions as FederatedAnalyticsPermission[],
|
|
238
|
+
revokedAt: grant.revokedAt,
|
|
239
|
+
} satisfies FederatedAnalyticsConnection;
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
export const {
|
|
244
|
+
manifest,
|
|
245
|
+
connectionStatus,
|
|
246
|
+
listSites,
|
|
247
|
+
overview,
|
|
248
|
+
liveVisitors,
|
|
249
|
+
listSessions,
|
|
250
|
+
sessionJourney,
|
|
251
|
+
listConversions,
|
|
252
|
+
visitorJourney,
|
|
253
|
+
goalsReport,
|
|
254
|
+
funnelsReport,
|
|
255
|
+
affiliatesReport,
|
|
256
|
+
vitalsReport,
|
|
257
|
+
siteMap,
|
|
258
|
+
dataCoverage,
|
|
259
|
+
siteSettings,
|
|
260
|
+
updateSite,
|
|
261
|
+
listGoals,
|
|
262
|
+
upsertGoal,
|
|
263
|
+
removeGoal,
|
|
264
|
+
listFunnels,
|
|
265
|
+
upsertFunnel,
|
|
266
|
+
removeFunnel,
|
|
267
|
+
listAffiliates,
|
|
268
|
+
upsertAffiliate,
|
|
269
|
+
removeAffiliate,
|
|
270
|
+
retentionStatus,
|
|
271
|
+
setRetentionPolicy,
|
|
272
|
+
disableRetentionPolicy,
|
|
273
|
+
} = federated;
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The resolver receives the authenticated `UserIdentity` as well as the parsed
|
|
277
|
+
connection and organization IDs, so a host may enforce additional local rules.
|
|
278
|
+
It must use an indexed, authoritative host lookup rather than token-provided
|
|
279
|
+
site IDs. A host that never lists `analytics:configure` in a grant may leave the
|
|
280
|
+
configure functions out of the export; the manifest still advertises them, and
|
|
281
|
+
the dashboard never calls them for a read-only connection.
|
|
282
|
+
|
|
283
|
+
## Stable errors
|
|
284
|
+
|
|
285
|
+
`FEDERATED_ANALYTICS_ERROR_CODES` exports all protocol v1 authorization codes:
|
|
286
|
+
|
|
287
|
+
| Code | Meaning |
|
|
288
|
+
| -------------------------------- | -------------------------------------------- |
|
|
289
|
+
| `FEDERATION_UNAUTHENTICATED` | No authenticated identity |
|
|
290
|
+
| `FEDERATION_ISSUER_MISMATCH` | Identity came from another issuer |
|
|
291
|
+
| `FEDERATION_INVALID_CLAIMS` | Required bounded claims or permission absent |
|
|
292
|
+
| `FEDERATION_CONNECTION_DENIED` | Connection missing, revoked, or malformed |
|
|
293
|
+
| `FEDERATION_INVALID_SCOPE` | Requested site list violates public limits |
|
|
294
|
+
| `FEDERATION_SITE_DENIED` | Requested site is outside the connection |
|
|
295
|
+
| `FEDERATION_CONFIGURE_FORBIDDEN` | Token or grant lacks `analytics:configure` |
|
|
296
|
+
|
|
297
|
+
Consumers should branch on `ConvexError.data.code`, not human-readable messages.
|
|
298
|
+
|
|
299
|
+
## Enforced invariants
|
|
300
|
+
|
|
301
|
+
- The identity issuer must match exactly.
|
|
302
|
+
- The token must include the read permission.
|
|
303
|
+
- A configure function runs only when the token and the grant both carry
|
|
304
|
+
`analytics:configure`; a grant listing an unknown permission is denied whole.
|
|
305
|
+
- Report queries must request one through ten unique, bounded site IDs.
|
|
306
|
+
- The resolved connection ID and organization must match the token.
|
|
307
|
+
- Revoked, missing, malformed, empty, or duplicate-site connections are denied.
|
|
308
|
+
- The resolved connection may authorize at most 10 unique site IDs.
|
|
309
|
+
- Every report site must exist in the local connection scope.
|
|
310
|
+
- Site discovery trusts only the resolved connection, never caller arguments.
|
|
311
|
+
- Owner-wide enumeration, ingestion, and arbitrary host functions are absent;
|
|
312
|
+
the only writes are the configure scope's, and `listSites` never returns
|
|
313
|
+
`ownerId`, `domains`, or `networkId`.
|
|
314
|
+
- `dataCoverage` is read-only and uses the same connection-scoped site
|
|
315
|
+
authorization.
|
|
316
|
+
|
|
317
|
+
`dataCoverage` accepts one authorized `siteId` and an inclusive integer
|
|
318
|
+
millisecond `from`/`to` range. It returns availability, rollup generation, and
|
|
319
|
+
retention boundaries for each dashboard dataset, but no events, visitor IDs,
|
|
320
|
+
session IDs, or site configuration. Dashboard clients should use it to explain
|
|
321
|
+
`complete`, `partial`, `retained`, and `unavailable` states rather than treating
|
|
322
|
+
an empty report as proof that no telemetry exists.
|
|
323
|
+
|
|
324
|
+
Availability is derived from bounded indexed source reads rather than a shared
|
|
325
|
+
per-ingest watermark. The dataset list distinguishes `overviewHour` from
|
|
326
|
+
`overviewDay`; clients must not apply one granularity's retention boundary to
|
|
327
|
+
the other. Heartbeats advance `sessions` availability only, not `events` or
|
|
328
|
+
either overview dataset.
|
|
329
|
+
|
|
330
|
+
## Control-plane status
|
|
331
|
+
|
|
332
|
+
The production control plane at `https://www.amagerastro.com` and its reference
|
|
333
|
+
implementation under `control-plane/` provide Better Auth sessions,
|
|
334
|
+
organization-scoped connections, OIDC discovery, JWKS, ten-minute RS256 tokens
|
|
335
|
+
whose permissions follow the member's organization role, manifest verification,
|
|
336
|
+
redacted site discovery, audit records, revocation of new token issuance, and
|
|
337
|
+
dynamic browser subscriptions to active deployments. Verification records the
|
|
338
|
+
permissions the host honors for an admin token, so **Conexões** shows whether a
|
|
339
|
+
connection is read only or reads and configures.
|
|
340
|
+
|
|
341
|
+
Do not assume that a dist-tag or source checkout contains this protocol. Inspect
|
|
342
|
+
an operator-approved exact npm registry artifact outside the host, without
|
|
343
|
+
lifecycle scripts, and statically verify every runtime and declaration export
|
|
344
|
+
listed by the machine manifest before changing a customer deployment. Automation
|
|
345
|
+
must stop at this gate rather than fetching source or reimplementing the
|
|
346
|
+
connector.
|
|
347
|
+
|
|
348
|
+
Remote local-grant provisioning is intentionally not an unauthenticated control-
|
|
349
|
+
plane write. The customer operator provisions the authoritative grant through an
|
|
350
|
+
internal or host-admin mutation. For immediate revocation, revoke that local
|
|
351
|
+
grant before revoking the central connection; otherwise an already-issued token
|
|
352
|
+
remains valid until its ten-minute expiry.
|