You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reuse clients across todaysand yesterday hashes, and rewrite yesterday matches
to today hash instead of introducing another identity column. This keeps the
identity model simple, preserves short-lived continuity across adjacent UTC
midnights, and still rotates a client once a UTC day was skipped.
Also move page-view dedup ahead of session updates so duplicate hits do not
inflate page_view_count, duration, or exit metrics. Add the migration to merge
duplicate clients, enforce unique (site_id, hash), and update tests/docs for the
new rotation strategy
Copy file name to clipboardExpand all lines: PRIVACY.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@ Lovely Eye is self-hosted analytics. The site owner is the data controller. This
14
14
15
15
## Visitor Identifiers
16
16
17
-
We derive a daily-rotating visitor identifier on the server. It is based on a keyed hash of site ID, truncated IP prefix, browser family, and device class. It changes every UTC dayand is not a persistent identifier. This keyed approach helps reduce the impact of database-only leaks because the stored analytics rows do not include enough information to recompute the identifier on their own.
17
+
We derive a keyed visitor identifier on the server from site ID, truncated IP prefix, browser family, and device class. The hash is computed per UTC day, but the server reuses the same client across `today`and `yesterday`; if only yesterday matches, that row is rewritten to today's hash. A new client is created only after a UTC day was skipped, so the identifier is still short-lived and not persistent. This keyed approach helps reduce the impact of database-only leaks because the stored analytics rows do not include enough information to recompute the identifier on their own.
-**Lightweight**: runtime consumes around ~15MB of RAM on AMD processor.
13
13
-**SQLite and PostgreSQL** supported.
@@ -146,7 +146,7 @@ After you started your containers:
146
146
147
147
Country tracking downloads the GeoIP database on demand when at least one site enables it. If the download fails, the dashboard will show the error in site settings.
148
148
149
-
Analytics visitor identity is server-generated, rotates daily in UTC, and is derived from a keyed hash of site ID, truncated IP prefix, browser family, and device class. Country tracking is kept separate from visitor identity. The dedicated analytics identity secret helps reduce the impact of database-only leaks, because visitor IDs cannot be recomputed from stored analytics data alone.
149
+
Analytics visitor identity is server-generatedand derived from a keyed hash of site ID, truncated IP prefix, browser family, and device class. The hash is computed per UTC day, but the server reuses the same client across `today` and `yesterday`; if only yesterday matches, that row is rewritten to today's hash. Country tracking stays separate from visitor identity, sessions still expire after 30 minutes of inactivity, and the dedicated analytics identity secret helps reduce the impact of database-only leaks because visitor IDs cannot be recomputed from stored analytics data alone.
Analytics e2e tests should use a fixed `ANALYTICS_IDENTITY_SECRET` so visitor identity stays deterministic across test runs.
9
+
Analytics e2e tests should use a fixed `ANALYTICS_IDENTITY_SECRET` so visitor identity stays deterministic across test runs, including the UTC-day-skipped `today`/`yesterday` client reuse path.
Copy file name to clipboardExpand all lines: server/internal/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,4 +14,4 @@ Modules:
14
14
-`./models` - Domain models with [Bun](https://github.com/uptrace/bun) annotations. Defines User, Site, Client, Session, Event, and event definition entities.
15
15
-`./repository` - Data access layer. Provides CRUD operations for all models using [Bun ORM](https://github.com/uptrace/bun).
16
16
-`./server` - Application bootstrap and HTTP server setup. Wires all dependencies and configures routes.
17
-
-`./services` - Business logic layer. Contains SiteService and AnalyticsService with domain operations, including pseudonymous visitor identity and session handling.
17
+
-`./services` - Business logic layer. Contains SiteService and AnalyticsService with domain operations, including pseudonymous visitor identity with UTC-day-skipped rotation and 30-minute session handling.
Copy file name to clipboardExpand all lines: server/internal/auth/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ No CSRF tokens needed. See [discussion](https://www.reddit.com/r/node/comments/1
29
29
| Variable | Default | Description |
30
30
|----------|---------|-------------|
31
31
|`JWT_SECRET`| generated at startup if empty | Secret key for signing tokens. Must be at least 32 characters when set. Set it explicitly in production if sessions should survive restarts. |
32
-
|`ANALYTICS_IDENTITY_SECRET`| falls back to `JWT_SECRET`| Optional dedicated secret for analytics visitor identity. Must be at least 32 characters when set. Helps reduce the impact of database-only leaks by making visitor IDs harder to recompute. |
32
+
|`ANALYTICS_IDENTITY_SECRET`| falls back to `JWT_SECRET`| Optional dedicated secret for analytics visitor identity. Must be at least 32 characters when set. Analytics uses it for the daily UTC hashes behind UTC-day-skipped rotation, and it helps reduce the impact of database-only leaks by making visitor IDs harder to recompute. |
33
33
|`JWT_ACCESS_EXPIRY_MINUTES`|`15`| Access token lifetime in minutes |
34
34
|`JWT_REFRESH_DAYS`|`7`| Refresh token lifetime in days |
35
35
|`SECURE_COOKIES`|`true`| Set to `true` in production (requires HTTPS) |
0 commit comments