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
{{ message }}
Repository navigation
Commit e14287a
Browse filesBrowse the repository at this point in the historyBrowse files
GET /log returns each row's client (ua, ipHash, referer), and logs where Cloudflare places it (city/region/asOrg)
`d1AuditQuery().recent()` now also selects `ua`, `ip_hash` and `referer`, which were already recorded. `requestMeta` reads `request.cf.{city, region, asOrganization}` into three new nullable columns (`migrations/0002_access_log_location.sql`, additive). IPs stay HMAC'd and are never stored raw. Spec from disk-tree m3, moved to `specs/done/`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyJyzSYpRxJuL6x6NyjpG3
Copy file name to clipboardExpand all lines: README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,7 +61,7 @@ Peer deps are all optional and only needed for what you use: `@cloudflare/worker
61
61
62
62
## Migrations
63
63
64
-
`migrations/0001_init.sql` is the whole schema. A fresh database applies it (`wrangler d1 migrations apply` with `migrations_dir` pointed at the package's `migrations/`, as the demo does, or its content copied into your own sequence alongside your app's tables).
64
+
`migrations/0001_init.sql` is the baseline schema, and `0002_access_log_location.sql` adds the log's location columns. A fresh database applies it (`wrangler d1 migrations apply` with `migrations_dir` pointed at the package's `migrations/`, as the demo does, or its content copied into your own sequence alongside your app's tables).
65
65
66
66
**Pre-1.0, migrations are squashed**: a schema change lands as a new numbered file, and once every known database has applied it, the directory is collapsed back into one baseline rather than kept as history. To move an existing database across a squash, bring it to the previous head (apply the pre-squash files from the SHA you're on), then mark the new files applied without running them:
67
67
@@ -157,7 +157,7 @@ A verified address that policy rejects redirects with `?denied=<email>` rather t
157
157
158
158
`gate.update(id, patch)` changes a link's terms after the fact — expiry, cap, TTL, memo — so extending a deadline doesn't mean minting and re-sending a second link. (`sessionTtlS` is baked into the cookie at redeem, so it only affects future redemptions.)
159
159
160
-
**The access log** is one store for auth-lifecycle events and (optionally) views, so "who viewed what" joins to `grants` natively. Lifecycle events always log; `view` events are deduped per (session, path, hour) by a partial unique index, and are **off by default** — turn them on alongside the "access is logged" disclosure copy, not silently. Client IPs are never stored, only `HMAC(ip, secret)`.
160
+
**The access log** is one store for auth-lifecycle events and (optionally) views, so "who viewed what" joins to `grants` natively. Lifecycle events always log; `view` events are deduped per (session, path, hour) by a partial unique index, and are **off by default** — turn them on alongside the "access is logged" disclosure copy, not silently. Client IPs are never stored, only `HMAC(ip, secret)`. In their place each row keeps where Cloudflare places the client (`request.cf` city, region, and network operator, e.g. "Comcast"), alongside the country, user agent and referer; `GET <basePath>/log` returns all of them, so an admin can tell whether a redeem came from the person the link was sent to.
161
161
162
162
**Magic links / passwordless sign-up.**`anyEmailPolicy` auto-approves any address, mints a grant bound to it, and hands it to `notify` — which becomes a real sign-in flow once `notify` can send mail:
Copy file name to clipboardExpand all lines: specs/done/log-client-fields.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,3 +27,11 @@ Ryan's call: keep IPs hashed, never stored raw, and log the location and network
27
27
## Consumer
28
28
29
29
disk-tree's `GrantLog` already renders `ua` / `ip_hash` when present (hidden columns otherwise); it will add `city`/`region`/`as_org` beside `country`. Once this ships as a dist build, disk-tree bumps `@open-athena/auth` in `site/package.json` and applies the new migration to its D1.
30
+
31
+
## As built (auth, 2026-10-07)
32
+
33
+
-`StoredEvent` (from `d1AuditQuery().recent()`, so `GET /log`) gains `ua`, `ipHash`, `referer`, `city`, `region`, `asOrg`, all nullable.
34
+
-`migrations/0002_access_log_location.sql`: three `ALTER TABLE access_log ADD COLUMN`s (`city`, `region`, `as_org`). Additive, so a prod D1 takes it in place. It stays a separate file until the next pre-1.0 squash.
35
+
-`requestMeta` reads `request.cf.{city, region, asOrganization}` (null off Cloudflare). It already feeds every logged event, so redeem, deny, view, request and sign-in rows all carry them. `access_log_daily` rollups are unchanged and still keyed by country.
36
+
- Tests: `routes.test.ts` "returns each row's client…" (a request with a `cf` object lands all three fields; one without leaves them null), and `schema.test.mjs` pins the new columns and file.
37
+
- disk-tree: bump `@open-athena/auth` to the dist build of this commit, then `wrangler d1 migrations apply` (or apply `0002` by hand if its D1 copies the package's SQL into its own sequence).
Copy file name to clipboardExpand all lines: src/core/audit.ts
+23-2Lines changed: 23 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -34,6 +34,11 @@ export interface AccessEvent {
34
34
ipHash?: string|null
35
35
ua?: string|null
36
36
country?: string|null
37
+
/** Where and on what network the client is, from Cloudflare's `request.cf`: enough to tell "the person I sent it to" from "someone else", without keeping an address. */
38
+
city?: string|null
39
+
region?: string|null
40
+
/** The client's network operator (`cf.asOrganization`), e.g. "Comcast" or "Amazon.com". */
41
+
asOrg?: string|null
37
42
referer?: string|null
38
43
/**
39
44
* Event detail. On `deny`: `expired`, `revoked`, `disabled`, `exhausted`,
0 commit comments