@iann29/rastro 0.4.0 → 0.6.0
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 +146 -24
- package/agent/integration.md +66 -29
- package/agent/manifest.json +19 -8
- package/agent/manifest.schema.json +28 -9
- package/dist/client/federation.d.ts +38 -8
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +18 -1
- package/dist/client/federation.js.map +1 -1
- package/dist/client/identity.d.ts +11 -0
- package/dist/client/identity.d.ts.map +1 -0
- package/dist/client/identity.js +123 -0
- package/dist/client/identity.js.map +1 -0
- package/dist/client/index.d.ts +501 -11
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +220 -4
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +8 -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 +158 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/constants.d.ts +2 -0
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +7 -0
- package/dist/component/constants.js.map +1 -1
- package/dist/component/coverage.d.ts +1 -0
- package/dist/component/coverage.d.ts.map +1 -1
- package/dist/component/coverage.js +6 -1
- package/dist/component/coverage.js.map +1 -1
- package/dist/component/eventStore.d.ts +2 -0
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +51 -1
- package/dist/component/http.js.map +1 -1
- package/dist/component/ingest.d.ts +2 -0
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +72 -22
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/origin.d.ts +70 -0
- package/dist/component/origin.d.ts.map +1 -0
- package/dist/component/origin.js +230 -0
- package/dist/component/origin.js.map +1 -0
- package/dist/component/people.d.ts +79 -0
- package/dist/component/people.d.ts.map +1 -0
- package/dist/component/people.js +249 -0
- package/dist/component/people.js.map +1 -0
- package/dist/component/platforms.d.ts +33 -0
- package/dist/component/platforms.d.ts.map +1 -0
- package/dist/component/platforms.js +328 -0
- package/dist/component/platforms.js.map +1 -0
- package/dist/component/reports.d.ts +35 -74
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +59 -14
- package/dist/component/reports.js.map +1 -1
- package/dist/component/sanitize.d.ts +5 -0
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +33 -1
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +101 -7
- package/dist/component/schema.js +45 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/trackedLinks.d.ts +91 -0
- package/dist/component/trackedLinks.d.ts.map +1 -0
- package/dist/component/trackedLinks.js +314 -0
- package/dist/component/trackedLinks.js.map +1 -0
- package/dist/component/useragent.d.ts +6 -0
- package/dist/component/useragent.d.ts.map +1 -1
- package/dist/component/useragent.js +9 -0
- package/dist/component/useragent.js.map +1 -1
- package/dist/component/validators.d.ts +101 -11
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +62 -1
- package/dist/component/validators.js.map +1 -1
- package/dist/component/visitors.d.ts +38 -2
- package/dist/component/visitors.d.ts.map +1 -1
- package/dist/component/visitors.js +162 -42
- package/dist/component/visitors.js.map +1 -1
- package/dist/react/index.d.ts +9 -5
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +36 -5
- package/dist/react/index.js.map +1 -1
- package/dist/tracker/generated.d.ts +6 -6
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +6 -6
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +3 -1
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +56 -5
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker.min.js +1 -1
- package/docs/federation.md +24 -0
- package/docs/identity.md +307 -0
- package/docs/upgrading.md +190 -19
- package/llms.txt +11 -2
- package/package.json +5 -3
- package/src/component/_generated/api.ts +8 -0
- package/src/component/_generated/component.ts +251 -1
- package/src/component/constants.ts +7 -0
- package/src/component/coverage.ts +7 -1
- package/src/component/http.ts +56 -1
- package/src/component/ingest.ts +100 -28
- package/src/component/origin.ts +273 -0
- package/src/component/people.ts +321 -0
- package/src/component/platforms.ts +359 -0
- package/src/component/reports.ts +84 -13
- package/src/component/sanitize.ts +39 -1
- package/src/component/schema.ts +53 -0
- package/src/component/trackedLinks.ts +384 -0
- package/src/component/useragent.ts +11 -0
- package/src/component/validators.ts +120 -0
- package/src/component/visitors.ts +232 -55
- package/src/tracker/generated.ts +6 -6
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/amageweb/amage-rastro/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Real-time product and web analytics built as an isolated Convex component.
|
|
6
6
|
|
|
7
|
-
Amage Rastro combines a
|
|
7
|
+
Amage Rastro combines a lightweight browser tracker with reactive traffic
|
|
8
8
|
metrics, a live visitor atlas, journey timelines, trusted payment attribution,
|
|
9
9
|
goals, funnels, affiliates, and multi-site reports. Data and functions stay
|
|
10
10
|
inside your Convex deployment.
|
|
@@ -55,7 +55,7 @@ capacity claim.
|
|
|
55
55
|
| Auto-captured clicks | Anchors, buttons, and `data-rastro-event` controls with a bounded visible label; outbound origin+path |
|
|
56
56
|
| Bot filtering | Self-declared crawlers, unfurlers, monitors, HTTP libraries, and headless browsers rejected at ingestion |
|
|
57
57
|
| Cookieless mode | No cookies: pseudonymous visitor id in `localStorage`; sessions end after 30 idle minutes or a sign-in |
|
|
58
|
-
| Tiny tracker | 2,
|
|
58
|
+
| Tiny tracker | 2,999 bytes raw, 1,565 bytes gzip; the test suite enforces `< 1,700` bytes gzip |
|
|
59
59
|
|
|
60
60
|
## Quickstart
|
|
61
61
|
|
|
@@ -82,7 +82,7 @@ Never append `/rastro/tracker.js` to the functions URL.
|
|
|
82
82
|
npm install @iann29/rastro
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
The untagged npm `latest` channel is `0.
|
|
85
|
+
The untagged npm `latest` channel is `0.6.0`. Pin the exact version in the host;
|
|
86
86
|
the changelog and the upgrading guide say what each release changes.
|
|
87
87
|
|
|
88
88
|
### 2. Mount the component
|
|
@@ -237,8 +237,9 @@ until it reloads.
|
|
|
237
237
|
|
|
238
238
|
The tracker sends no client identification. Ingestion derives `browser`, `os`,
|
|
239
239
|
and `device` from headers the browser attaches itself, and persists only those
|
|
240
|
-
three normalized values;
|
|
241
|
-
|
|
240
|
+
three normalized values; it also reads the in-app browser's platform id
|
|
241
|
+
(`instagram`, `facebook`, `tiktok`…) for the session's origin, below. The raw
|
|
242
|
+
`User-Agent` never reaches a mutation or a component table.
|
|
242
243
|
|
|
243
244
|
Chromium attaches the low-entropy `Sec-CH-UA`, `Sec-CH-UA-Mobile`, and
|
|
244
245
|
`Sec-CH-UA-Platform` client hints to cross-origin requests from secure origins,
|
|
@@ -499,10 +500,11 @@ export function Analytics({ siteId, siteUrl, analyticsId }) {
|
|
|
499
500
|
}
|
|
500
501
|
```
|
|
501
502
|
|
|
502
|
-
`visitorId`
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
503
|
+
Supply a server-issued `visitorId` before the first pageview when available. For
|
|
504
|
+
login after tracking starts, use the `identify` command described in the
|
|
505
|
+
[identity guide](docs/identity.md); it closes the previous session before
|
|
506
|
+
switching IDs. Call `reset` on logout. Released 0.5.x trackers require a reload
|
|
507
|
+
to change the initial ID.
|
|
506
508
|
|
|
507
509
|
### 5. Verify ingestion
|
|
508
510
|
|
|
@@ -577,19 +579,132 @@ payload-bound by the site's financial ledger.
|
|
|
577
579
|
The tracker reads `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, and
|
|
578
580
|
`utm_content` from the landing URL, keeps them in `sessionStorage` for the rest
|
|
579
581
|
of the tab — a later session the tab opens after a sign-in or 30 idle minutes
|
|
580
|
-
keeps them too — and sends them as properties of every pageview
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
the
|
|
585
|
-
`
|
|
586
|
-
|
|
587
|
-
the
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
582
|
+
keeps them too — and sends them as properties of every pageview. Besides those
|
|
583
|
+
five, the only thing the landing URL ever yields is the **name** of a click-ID
|
|
584
|
+
key it carried (below); no other query parameter, and no query value at all,
|
|
585
|
+
ever leaves the page. A session's `source` is the lowercase `utm_source` when
|
|
586
|
+
present and the referrer host otherwise, so campaign traffic no longer reads as
|
|
587
|
+
`direct`. `utmSource`, `utmMedium`, and `utmCampaign` stay on the session for
|
|
588
|
+
journeys; `overview.topCampaigns` ranks `utm_campaign` and `overview.topMediums`
|
|
589
|
+
the lowercase `utm_medium` next to `topSources`, by sessions once the range is
|
|
590
|
+
covered by the counters. `overview.topOutbound` ranks the hosts outbound clicks
|
|
591
|
+
reached — the host alone, never the path or query — by clicks. Both lists are
|
|
592
|
+
recorded from `metadata.dimensionsSince` (each site's first ingest on 0.1.0 plus
|
|
593
|
+
the rollup delay); a range that starts earlier ranks them from partial data, and
|
|
594
|
+
the dashboard says so.
|
|
595
|
+
|
|
596
|
+
A session also keeps where it came from, classified once on its first touch and
|
|
597
|
+
never guessed: `platform` (an id from a closed, versioned table of about forty —
|
|
598
|
+
`instagram`, `google`, `whatsapp`, `chatgpt` — or `unknown` for a host or
|
|
599
|
+
`utm_source` outside it, or `none`), `channel` (`direct`, `organic_search`,
|
|
600
|
+
`paid_search`, `organic_social`, `paid_social`, `messaging`, `email`,
|
|
601
|
+
`affiliate`, `ai`, `display`, `referral`, `offline`), and `evidence`, the rule
|
|
602
|
+
that decided it. One rule decides and the next only runs when it did not:
|
|
603
|
+
`utm_source` through the alias table, then click-ID names, then the referrer
|
|
604
|
+
host by registrable domain, then the in-app browser, then an affiliate `ref`,
|
|
605
|
+
which sets the channel and keeps the platform an earlier rule found. Hosts match
|
|
606
|
+
by whole label, never by substring, so `l.instagram.com`, `instagram.com` and
|
|
607
|
+
`com.instagram.android` are one platform while `docs.google.com` is not Google;
|
|
608
|
+
`utm_medium` picks the channel from a closed table that also accepts the channel
|
|
609
|
+
ids themselves, which is what a tracked link (below) emits, `offline` included.
|
|
610
|
+
Nothing at all is `direct`, the only way to be direct, and a referrer on the
|
|
611
|
+
site's own domains is an internal hop, never an origin. `overview.topPlatforms`,
|
|
612
|
+
`topChannels` and `topEvidence` rank them beside `topSources`; a `topPlatforms`
|
|
613
|
+
value is the platform id alone when the session arrived by that platform's
|
|
614
|
+
default channel and `platform@channel` when it did not (`google@paid_search`),
|
|
615
|
+
so one platform ranks once per channel it was reached by —
|
|
616
|
+
`splitPlatformDimension` reads the pair back. `dataCoverage`'s `origin` dataset
|
|
617
|
+
says from when a site carries them; sessions older than that are classified on
|
|
618
|
+
read and marked `evidence: "legacy"`. `source` keeps the raw `utm_source` or
|
|
619
|
+
host for the drill-down, exactly as before.
|
|
620
|
+
|
|
621
|
+
The tracker reads the click-ID keys of its landing URL and sends them in `clid`:
|
|
622
|
+
their **names** only (`fbclid`, `gclid`, `ttclid`, `mc_cid`…), never their
|
|
623
|
+
values, so no advertising identifier leaves the page. It matches the same
|
|
624
|
+
allowlist the server does, in the same ranking order, and sends at most eight;
|
|
625
|
+
ingestion drops any name outside the allowlist and keeps at most eight of what
|
|
626
|
+
is left. The referrer it sends is scheme and host, so a referrer of the form
|
|
627
|
+
`android-app://<package>` keeps the package alone and the app that opened the
|
|
628
|
+
link is a platform (`com.google.android.gm` is Gmail) instead of nothing.
|
|
629
|
+
|
|
630
|
+
## Tracked links
|
|
631
|
+
|
|
632
|
+
Some traffic arrives with nothing to read. A link tapped inside WhatsApp,
|
|
633
|
+
Telegram, an SMS or a native e-mail app, or scanned from a QR code, opens with
|
|
634
|
+
no referrer and no campaign, and any analytics tool files it as `direct`. A
|
|
635
|
+
tracked link closes that gap: the dashboard's **Links** tab (or the host API)
|
|
636
|
+
creates `/rastro/l/<slug>` for one place the link will be published, and every
|
|
637
|
+
click lands on the site with that place in its `utm_*`.
|
|
638
|
+
|
|
639
|
+
`GET /rastro/l/<slug>` answers `302` with `Cache-Control: no-store` to the
|
|
640
|
+
link's destination, after appending:
|
|
641
|
+
|
|
642
|
+
| Parameter | Value |
|
|
643
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
644
|
+
| `utm_source` | the platform: `instagram`, `whatsapp`, `qr`, or any id of 2–24 lowercase letters, digits or underscores |
|
|
645
|
+
| `utm_medium` | the channel: `organic_social`, `paid_social`, `messaging`, `email`, `affiliate`, `display`, `referral` or `offline` |
|
|
646
|
+
| `utm_campaign` | the campaign, or the slug when the link has none |
|
|
647
|
+
| `utm_content` | the slug |
|
|
648
|
+
|
|
649
|
+
The destination's own query stays exactly as it was, and a `utm_*` key it
|
|
650
|
+
already carries is never overridden. Unknown and disabled slugs answer `404` in
|
|
651
|
+
plain text. The tracker needs nothing new: the landing reads the `utm_*` like
|
|
652
|
+
any campaign link.
|
|
653
|
+
|
|
654
|
+
- **Clicks and bots.** Each click lands in a daily counter spread over four
|
|
655
|
+
shards, so a link that goes viral never serializes on one document. A
|
|
656
|
+
self-declared bot, including the WhatsApp, Facebook and Telegram link
|
|
657
|
+
previews, is still redirected but counted under `bots`, never `clicks`.
|
|
658
|
+
`listTrackedLinks` reports both, all time and over the 30 UTC days ending on
|
|
659
|
+
the day of the `now` it is given.
|
|
660
|
+
- **No open redirect.** A destination must be an absolute `http(s)` URL without
|
|
661
|
+
credentials on one of the site's domains (exact, or a `*.` pattern for
|
|
662
|
+
subdomains). The rule is checked when a link is created or changed and again
|
|
663
|
+
on every click, so a domain removed from the site stops redirecting at once.
|
|
664
|
+
- **Slugs** are 3–32 lowercase letters, digits or hyphens, starting and ending
|
|
665
|
+
with a letter or digit, and unique across the deployment because the route
|
|
666
|
+
names no site. A site keeps at most 200 links. Deleting a link frees its slug
|
|
667
|
+
and removes its counters; disabling it keeps both.
|
|
668
|
+
- **A short domain.** The route lives on the deployment's HTTP origin
|
|
669
|
+
(`https://<deployment>.convex.site/rastro/l/promo`). A host that wants a
|
|
670
|
+
presentable link serves that origin under a short domain of its own, such as
|
|
671
|
+
`go.example.com/rastro/l/promo`; nothing in the component changes.
|
|
672
|
+
|
|
673
|
+
```ts
|
|
674
|
+
await analytics.createTrackedLink(ctx, {
|
|
675
|
+
siteId,
|
|
676
|
+
slug: "bio-instagram",
|
|
677
|
+
destination: "https://www.example.com/promo",
|
|
678
|
+
platform: "instagram",
|
|
679
|
+
channel: "organic_social",
|
|
680
|
+
campaign: "spring-sale",
|
|
681
|
+
createdBy: userId, // an opaque id, never an e-mail address
|
|
682
|
+
});
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
A federated dashboard reaches the same functions once the host re-exports
|
|
686
|
+
`listTrackedLinks`, `createTrackedLink`, `updateTrackedLink`, and
|
|
687
|
+
`deleteTrackedLink` from `convex/rastroFederation.ts`. The connection then
|
|
688
|
+
advertises the `trackedLinks` capability: any reader lists the links, and
|
|
689
|
+
changing them needs `analytics:configure`, like goals and affiliates.
|
|
690
|
+
|
|
691
|
+
## Identify people with any authentication
|
|
692
|
+
|
|
693
|
+
Rastro connects names and emails to visits, actions and conversions. Use
|
|
694
|
+
`identifyVisitorMutation` with your backend's authenticated-user resolver for
|
|
695
|
+
Better Auth, custom auth or another Convex JWT provider. Other backends use
|
|
696
|
+
`identifyVisitorHttpAction` over authenticated HTTP. No auth provider is a
|
|
697
|
+
runtime requirement of the SDK.
|
|
698
|
+
|
|
699
|
+
See the [identity integration guide](docs/identity.md) for the complete
|
|
700
|
+
server/browser flow, login/logout behavior, profile updates, and examples. The
|
|
701
|
+
dashboard presents profiles in Ao vivo and Jornadas, and the Pessoas directory
|
|
702
|
+
searches all profiles by name, email, ID or an exact typed attribute. Attributes
|
|
703
|
+
are optional strings, numbers and booleans defined by your project. Each person
|
|
704
|
+
opens a history scoped to their site and the selected period. These additions
|
|
705
|
+
require 0.6.0 or later; see [upgrade notes](docs/upgrading.md).
|
|
706
|
+
|
|
707
|
+
### Link existing visitor IDs
|
|
593
708
|
|
|
594
709
|
Anonymous sessions carry a random `visitorId` until the host supplies a
|
|
595
710
|
pseudonymous one through `data-visitor` or the `RastroScript` `visitorId` prop.
|
|
@@ -754,6 +869,8 @@ The `Rastro` class wraps the component boundary for host functions:
|
|
|
754
869
|
- Configuration: `upsertGoal`, `listGoals`, `removeGoal`, `upsertFunnel`,
|
|
755
870
|
`listFunnels`, `removeFunnel`, `upsertAffiliate`, `listAffiliates`,
|
|
756
871
|
`removeAffiliate`
|
|
872
|
+
- Tracked links: `createTrackedLink`, `updateTrackedLink`, `deleteTrackedLink`,
|
|
873
|
+
`listTrackedLinks`
|
|
757
874
|
- Retention: `cleanup`, `setRetentionPolicy`, `disableRetentionPolicy`,
|
|
758
875
|
`runRetentionPolicy`, `retentionStatus`
|
|
759
876
|
|
|
@@ -1049,7 +1166,12 @@ Amage Rastro's default tracker:
|
|
|
1049
1166
|
clicked link or button (at most 64 characters, replaced by `data-rastro-label`
|
|
1050
1167
|
when present), and outbound link origins and pathnames (the overview ranks
|
|
1051
1168
|
destinations by host alone);
|
|
1052
|
-
- strips query strings and fragments from stored paths and links
|
|
1169
|
+
- strips query strings and fragments from stored paths and links; of the landing
|
|
1170
|
+
query it keeps the five `utm_*` values and the bare **names** of the
|
|
1171
|
+
advertising click-ID keys it recognizes (`fbclid`, `gclid`…), never a click-ID
|
|
1172
|
+
value, and never any other parameter;
|
|
1173
|
+
- sends the referrer as scheme and host only — an `android-app://<package>`
|
|
1174
|
+
included — never its path or query.
|
|
1053
1175
|
|
|
1054
1176
|
The optional Web Vitals module inherits this contract: it reports at most five
|
|
1055
1177
|
timing numbers per page load (milliseconds, or CLS scaled by 1000) against the
|
package/agent/integration.md
CHANGED
|
@@ -494,6 +494,8 @@ export const {
|
|
|
494
494
|
overview,
|
|
495
495
|
liveVisitors,
|
|
496
496
|
getSession,
|
|
497
|
+
getVisitorProfile,
|
|
498
|
+
listPeople,
|
|
497
499
|
revenueSummary,
|
|
498
500
|
listSessions,
|
|
499
501
|
sessionJourney,
|
|
@@ -519,21 +521,25 @@ export const {
|
|
|
519
521
|
retentionStatus,
|
|
520
522
|
setRetentionPolicy,
|
|
521
523
|
disableRetentionPolicy,
|
|
524
|
+
listTrackedLinks,
|
|
525
|
+
createTrackedLink,
|
|
526
|
+
updateTrackedLink,
|
|
527
|
+
deleteTrackedLink,
|
|
522
528
|
} = federated;
|
|
523
529
|
```
|
|
524
530
|
|
|
525
531
|
`manifest` is public static metadata. Every report function is read-only and
|
|
526
532
|
requires both a valid JWT and a matching, non-revoked local grant. The functions
|
|
527
|
-
from `siteSettings`
|
|
528
|
-
`configure`, hosts from `alpha.11`): they additionally
|
|
529
|
-
`analytics:configure` on the token — the control plane claims it only
|
|
530
|
-
organization owners and admins — and in the grant's `permissions`, and fail
|
|
531
|
-
`FEDERATION_CONFIGURE_FORBIDDEN` otherwise. Export them even when every
|
|
532
|
-
stays read only, so a later opt-in needs no host deploy; the dashboard
|
|
533
|
-
calls them for a read-only connection. The issuer falls back to the
|
|
534
|
-
control plane so the module compiles and pushes before the
|
|
535
|
-
exists; tokens are still rejected until `auth.config.ts`
|
|
536
|
-
which needs both variables set and a push afterwards.
|
|
533
|
+
from `siteSettings` to `disableRetentionPolicy` form the configure scope
|
|
534
|
+
(optional capability `configure`, hosts from `alpha.11`): they additionally
|
|
535
|
+
require `analytics:configure` on the token — the control plane claims it only
|
|
536
|
+
for organization owners and admins — and in the grant's `permissions`, and fail
|
|
537
|
+
with `FEDERATION_CONFIGURE_FORBIDDEN` otherwise. Export them even when every
|
|
538
|
+
grant stays read only, so a later opt-in needs no host deploy; the dashboard
|
|
539
|
+
never calls them for a read-only connection. The issuer falls back to the
|
|
540
|
+
production control plane so the module compiles and pushes before the
|
|
541
|
+
environment variable exists; tokens are still rejected until `auth.config.ts`
|
|
542
|
+
trusts the same issuer, which needs both variables set and a push afterwards.
|
|
537
543
|
|
|
538
544
|
A public link's token carries `analytics:public` instead of `analytics:read` and
|
|
539
545
|
`rastro_site_ids` naming the link's sites. It runs only `manifest`,
|
|
@@ -547,25 +553,27 @@ permission rejects the token with `FEDERATION_INVALID_CLAIMS`.
|
|
|
547
553
|
|
|
548
554
|
## Function contract
|
|
549
555
|
|
|
550
|
-
| Function
|
|
551
|
-
|
|
|
552
|
-
| `manifest`
|
|
553
|
-
| `connectionStatus`
|
|
554
|
-
| `listSites`
|
|
555
|
-
| `overview`
|
|
556
|
-
| `liveVisitors`
|
|
557
|
-
| `getSession`
|
|
558
|
-
| `
|
|
559
|
-
| `
|
|
560
|
-
| `
|
|
561
|
-
| `
|
|
562
|
-
| `
|
|
563
|
-
| `
|
|
564
|
-
| `
|
|
565
|
-
| `
|
|
566
|
-
| `
|
|
567
|
-
| `
|
|
568
|
-
| `
|
|
556
|
+
| Function | Input summary | Important output/behavior |
|
|
557
|
+
| ------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
558
|
+
| `manifest` | `{}` | Static protocol, capabilities, names, and limits; no auth |
|
|
559
|
+
| `connectionStatus` | `{}` | Redacted status, protocol version, capabilities, site count |
|
|
560
|
+
| `listSites` | `{}` | Only `siteId`, `name`, `currency`, `timezone`, `cookieless` |
|
|
561
|
+
| `overview` | `siteIds`, `from`, `to`, optional hour/day interval | Aggregate metrics and time series |
|
|
562
|
+
| `liveVisitors` | `siteIds`, `now`, optional limit | At most 500 visitors seen in the last five minutes, with intent facts |
|
|
563
|
+
| `getSession` | one `siteId`, `sessionId` | Selected session metadata, or null when no retained session exists |
|
|
564
|
+
| `getVisitorProfile` | one `siteId`, `visitorId` | Identified profile through the optional `visitorProfiles` capability; reader permission required |
|
|
565
|
+
| `listPeople` | one `siteId`, optional `filter`, `paginationOpts` | Indexed profile directory through optional `people`; reader permission required; up to 50 rows per page |
|
|
566
|
+
| `revenueSummary` | `siteIds`, inclusive `from`/`to` | Period commissions; pair with overview totals/currency |
|
|
567
|
+
| `listSessions` | one `siteId`, range, `{numItems,cursor}` keyset options | Paginated sessions |
|
|
568
|
+
| `sessionJourney` | one site/session, range, `{numItems,cursor}` options | Paginated bounded events |
|
|
569
|
+
| `listConversions` | one site, range, `{numItems,cursor}` options | Paginated trusted server-side conversion ledger |
|
|
570
|
+
| `visitorJourney` | site IDs, visitor ID, range, optional limit | Cross-site journey inside the grant only |
|
|
571
|
+
| `goalsReport` | one site and range | Read-only goal definitions plus aggregates |
|
|
572
|
+
| `funnelsReport` | one site and complete UTC-day range | Read-only definitions/steps plus aggregates; max 90 days |
|
|
573
|
+
| `affiliatesReport` | one site and complete UTC-day range | Read-only affiliate definitions plus aggregates |
|
|
574
|
+
| `vitalsReport` | one site and complete UTC-day range | Web Vitals p75/ratings per metric, page, and device; max 90 days |
|
|
575
|
+
| `siteMap` | site IDs and complete UTC-day range | Routes with derived exits/bounces plus transitions; max 90 days |
|
|
576
|
+
| `dataCoverage` | one site and inclusive integer range | Coverage metadata; echoes site ID, no event/visitor/session IDs |
|
|
569
577
|
|
|
570
578
|
The configure scope, every function of which requires `analytics:configure` on
|
|
571
579
|
the token and in the grant:
|
|
@@ -587,6 +595,18 @@ the token and in the grant:
|
|
|
587
595
|
| `setRetentionPolicy` | `siteId` and five day counts between 1 and 3650 | Starts the recurring cleanup chains |
|
|
588
596
|
| `disableRetentionPolicy` | one `siteId` | Stops automatic cleanup; nothing is restored |
|
|
589
597
|
|
|
598
|
+
Tracked links, behind the optional `trackedLinks` capability. Listing needs only
|
|
599
|
+
a reader; the three writes require `analytics:configure` like the configure
|
|
600
|
+
scope above. A host serves them by re-exporting the four functions; one that
|
|
601
|
+
does not keeps working and advertises no `trackedLinks` capability.
|
|
602
|
+
|
|
603
|
+
| Function | Input summary | Important output/behavior |
|
|
604
|
+
| ------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
605
|
+
| `listTrackedLinks` | one `siteId` and `now` | Definitions with clicks and bot hits, all time and over 30 UTC days |
|
|
606
|
+
| `createTrackedLink` | `siteId`, slug, destination, platform, channel, campaign, `createdBy` | Returns the link ID; the destination must be on one of the site's domains |
|
|
607
|
+
| `updateTrackedLink` | `siteId`, `linkId`, optional destination, platform, channel, campaign, disabled | Changes everything but the slug; a disabled link answers 404 |
|
|
608
|
+
| `deleteTrackedLink` | `siteId`, `linkId` | Deletes the link, frees its slug, and purges its click counters |
|
|
609
|
+
|
|
590
610
|
The federated surface deliberately excludes ingestion, owner IDs, network IDs,
|
|
591
611
|
and arbitrary host functions, and shows a site's domains only through
|
|
592
612
|
`siteSettings` to a connection allowed to change them. Goal, funnel, and
|
|
@@ -821,3 +841,20 @@ Production changes performed: yes/no
|
|
|
821
841
|
|
|
822
842
|
Do not include private keys, admin keys, session cookies, JWTs, or raw customer
|
|
823
843
|
telemetry in the report.
|
|
844
|
+
|
|
845
|
+
## Identified people
|
|
846
|
+
|
|
847
|
+
Follow
|
|
848
|
+
[the identity guide](https://www.amagerastro.com/docs/reference/identity.md) to
|
|
849
|
+
integrate Better Auth, custom authentication or an external backend. The host
|
|
850
|
+
owns authenticated-user resolution and site authorization. Use
|
|
851
|
+
`Rastro.identifyVisitorMutation` for the current user, `identifyVisitor` from
|
|
852
|
+
trusted server code, and `identifyVisitorHttpAction` for authenticated
|
|
853
|
+
server-to-server HTTP. Attributes are optional, project-defined strings, numbers
|
|
854
|
+
and booleans; no user-table shape or business field is required. Re-export
|
|
855
|
+
`getVisitorProfile` and `listPeople` for the optional `visitorProfiles` and
|
|
856
|
+
`people` capabilities. Existing profiles need the bounded, owner-authorized
|
|
857
|
+
`backfillVisitorProfiles` upgrade before name/email search; see the identity
|
|
858
|
+
guide. Live/session reports may carry `profile`; public live reports remove it.
|
|
859
|
+
Apply the server-returned visitor ID in the tracker, and reset the tracker on
|
|
860
|
+
logout.
|
package/agent/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://www.amagerastro.com/agent/manifest.schema.json",
|
|
3
3
|
"name": "amage-rastro",
|
|
4
4
|
"title": "Amage Rastro",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Real-time product and web analytics with identified people, journeys and conversions; profiles and telemetry stay in each product deployment.",
|
|
6
6
|
"manifestVersion": "1.0.0",
|
|
7
7
|
"language": "en",
|
|
8
8
|
"canonicalBaseUrl": "https://www.amagerastro.com",
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
"upgrading": "https://www.amagerastro.com/docs/reference/upgrading.md",
|
|
16
16
|
"integrity": {
|
|
17
17
|
"algorithm": "sha256",
|
|
18
|
-
"agent": "
|
|
19
|
-
"llms": "
|
|
20
|
-
"protocol": "
|
|
18
|
+
"agent": "35d1c2d73eef8c5266368453dd4eb1343ecc70650e0c0b1d6e7dfcae4b5ecdcc",
|
|
19
|
+
"llms": "71be7d69dec942407cbe08db75490de50e2b3b399cdd611049cfb6d99b551e13",
|
|
20
|
+
"protocol": "d56dd4dca8c11a4d00303c48abfbcde4ed7cee6f415b2c96b971ba9efff2467c",
|
|
21
21
|
"setup": "bf2509a87a8eab9c047276b813f029bb2c9ddd58c0e54754432e3fef7891f3c9",
|
|
22
|
-
"upgrading": "
|
|
23
|
-
|
|
22
|
+
"upgrading": "4b72c250839e159b07c345ef7c71f2fdfe16a50470790fa5154cb6eaa6b72300",
|
|
23
|
+
"identity": "2d23399ae4e469b505790d9c7cc8a2012da96a49bd2e776a8118b51b52c8390e"
|
|
24
|
+
},
|
|
25
|
+
"identity": "https://www.amagerastro.com/docs/reference/identity.md"
|
|
24
26
|
},
|
|
25
27
|
"controlPlane": {
|
|
26
28
|
"dashboardUrl": "https://www.amagerastro.com",
|
|
@@ -85,7 +87,13 @@
|
|
|
85
87
|
"removeAffiliate": "removeAffiliate",
|
|
86
88
|
"retentionStatus": "retentionStatus",
|
|
87
89
|
"setRetentionPolicy": "setRetentionPolicy",
|
|
88
|
-
"disableRetentionPolicy": "disableRetentionPolicy"
|
|
90
|
+
"disableRetentionPolicy": "disableRetentionPolicy",
|
|
91
|
+
"listTrackedLinks": "listTrackedLinks",
|
|
92
|
+
"createTrackedLink": "createTrackedLink",
|
|
93
|
+
"updateTrackedLink": "updateTrackedLink",
|
|
94
|
+
"deleteTrackedLink": "deleteTrackedLink",
|
|
95
|
+
"getVisitorProfile": "getVisitorProfile",
|
|
96
|
+
"listPeople": "listPeople"
|
|
89
97
|
},
|
|
90
98
|
"capabilities": [
|
|
91
99
|
"siteSummaries",
|
|
@@ -100,11 +108,14 @@
|
|
|
100
108
|
"affiliates",
|
|
101
109
|
"dataCoverage",
|
|
102
110
|
"sessionDetails",
|
|
111
|
+
"visitorProfiles",
|
|
112
|
+
"people",
|
|
103
113
|
"revenueSummary",
|
|
104
114
|
"vitals",
|
|
105
115
|
"siteMap",
|
|
106
116
|
"localDays",
|
|
107
|
-
"configure"
|
|
117
|
+
"configure",
|
|
118
|
+
"trackedLinks"
|
|
108
119
|
],
|
|
109
120
|
"limits": {
|
|
110
121
|
"maxSitesPerRequest": 10,
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"protocol",
|
|
45
45
|
"setup",
|
|
46
46
|
"upgrading",
|
|
47
|
-
"integrity"
|
|
47
|
+
"integrity",
|
|
48
|
+
"identity"
|
|
48
49
|
],
|
|
49
50
|
"properties": {
|
|
50
51
|
"human": { "$ref": "#/$defs/httpsUrl" },
|
|
@@ -61,7 +62,8 @@
|
|
|
61
62
|
"llms",
|
|
62
63
|
"protocol",
|
|
63
64
|
"setup",
|
|
64
|
-
"upgrading"
|
|
65
|
+
"upgrading",
|
|
66
|
+
"identity"
|
|
65
67
|
],
|
|
66
68
|
"properties": {
|
|
67
69
|
"algorithm": { "const": "sha256" },
|
|
@@ -69,10 +71,12 @@
|
|
|
69
71
|
"llms": { "$ref": "#/$defs/sha256" },
|
|
70
72
|
"protocol": { "$ref": "#/$defs/sha256" },
|
|
71
73
|
"setup": { "$ref": "#/$defs/sha256" },
|
|
72
|
-
"upgrading": { "$ref": "#/$defs/sha256" }
|
|
74
|
+
"upgrading": { "$ref": "#/$defs/sha256" },
|
|
75
|
+
"identity": { "$ref": "#/$defs/sha256" }
|
|
73
76
|
},
|
|
74
77
|
"additionalProperties": false
|
|
75
|
-
}
|
|
78
|
+
},
|
|
79
|
+
"identity": { "$ref": "#/$defs/httpsUrl" }
|
|
76
80
|
},
|
|
77
81
|
"additionalProperties": false
|
|
78
82
|
},
|
|
@@ -185,7 +189,13 @@
|
|
|
185
189
|
"removeAffiliate",
|
|
186
190
|
"retentionStatus",
|
|
187
191
|
"setRetentionPolicy",
|
|
188
|
-
"disableRetentionPolicy"
|
|
192
|
+
"disableRetentionPolicy",
|
|
193
|
+
"listTrackedLinks",
|
|
194
|
+
"createTrackedLink",
|
|
195
|
+
"updateTrackedLink",
|
|
196
|
+
"deleteTrackedLink",
|
|
197
|
+
"getVisitorProfile",
|
|
198
|
+
"listPeople"
|
|
189
199
|
],
|
|
190
200
|
"properties": {
|
|
191
201
|
"manifest": { "const": "manifest" },
|
|
@@ -218,14 +228,20 @@
|
|
|
218
228
|
"removeAffiliate": { "const": "removeAffiliate" },
|
|
219
229
|
"retentionStatus": { "const": "retentionStatus" },
|
|
220
230
|
"setRetentionPolicy": { "const": "setRetentionPolicy" },
|
|
221
|
-
"disableRetentionPolicy": { "const": "disableRetentionPolicy" }
|
|
231
|
+
"disableRetentionPolicy": { "const": "disableRetentionPolicy" },
|
|
232
|
+
"listTrackedLinks": { "const": "listTrackedLinks" },
|
|
233
|
+
"createTrackedLink": { "const": "createTrackedLink" },
|
|
234
|
+
"updateTrackedLink": { "const": "updateTrackedLink" },
|
|
235
|
+
"deleteTrackedLink": { "const": "deleteTrackedLink" },
|
|
236
|
+
"getVisitorProfile": { "const": "getVisitorProfile" },
|
|
237
|
+
"listPeople": { "const": "listPeople" }
|
|
222
238
|
},
|
|
223
239
|
"additionalProperties": false
|
|
224
240
|
},
|
|
225
241
|
"capabilities": {
|
|
226
242
|
"type": "array",
|
|
227
|
-
"minItems":
|
|
228
|
-
"maxItems":
|
|
243
|
+
"minItems": 16,
|
|
244
|
+
"maxItems": 20,
|
|
229
245
|
"uniqueItems": true,
|
|
230
246
|
"items": {
|
|
231
247
|
"enum": [
|
|
@@ -245,7 +261,10 @@
|
|
|
245
261
|
"vitals",
|
|
246
262
|
"siteMap",
|
|
247
263
|
"localDays",
|
|
248
|
-
"configure"
|
|
264
|
+
"configure",
|
|
265
|
+
"trackedLinks",
|
|
266
|
+
"visitorProfiles",
|
|
267
|
+
"people"
|
|
249
268
|
]
|
|
250
269
|
}
|
|
251
270
|
},
|