Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2931159
Add email Analytics Engine reporting events
cursoragent Jul 31, 2026
cddc36b
Move admin reporting and audit storage off app D1
cursoragent Jul 31, 2026
c8be8ae
Map reporting storage primitives
cursoragent Jul 31, 2026
0bf7ca6
Handle absent audit binding in standalone contexts
cursoragent Jul 31, 2026
78d3996
Initialize audit database in local and worker tests
cursoragent Jul 31, 2026
4f13c1d
Load audit migrations into worker test runtime
cursoragent Jul 31, 2026
98855b4
Keep generic worker tests off audit persistence
cursoragent Jul 31, 2026
9be54ab
Pass audit database explicitly at persisted sinks
cursoragent Jul 31, 2026
f69304f
Format reporting storage changes
cursoragent Jul 31, 2026
d336100
Merge remote-tracking branch 'origin/main' into cursor/reporting-off-…
cursoragent Aug 1, 2026
a91a355
Route delegated package audit events to audit D1
cursoragent Aug 1, 2026
09e7d7d
Detect and retry partial audit writes
cursoragent Aug 1, 2026
63f8272
Merge remote-tracking branch 'origin/main' into cursor/reporting-off-…
cursoragent Aug 1, 2026
8110775
Cut audit reads and writes over to audit D1
cursoragent Aug 1, 2026
8e8df25
Migrate audit D1 for end-to-end tests
cursoragent Aug 1, 2026
c0d8104
Report missing production audit binding
cursoragent Aug 1, 2026
6422606
Preserve explicit audit opt-out in worker tests
cursoragent Aug 1, 2026
bb787ac
Merge remote-tracking branch 'origin/main' into cursor/reporting-off-…
cursoragent Aug 1, 2026
96c10f1
Drop legacy app database audit table
cursoragent Aug 1, 2026
a487823
Format audit storage documentation
cursoragent Aug 1, 2026
f9bd3f3
Isolate audit database in admin tests
cursoragent Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/contributing/architecture/data-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -1731,11 +1731,11 @@ Current retention policies:
resolved or dismissed, or the submitter deletes their account. Resolved and
dismissed rows keep 365 days after `updated_at`; submitter deletion removes
any remaining rows.
- `audit_events`: global hashed auth/security audit events are dual-written to
the legacy `APP_DB` table and the dedicated `AUDIT_DB` database during the
expand phase. Retention prunes only `AUDIT_DB` after 180 days; the legacy
table remains in place until a later contract phase. Audit events are not
user-owned rows and remain independent of account deletion/export.
- `audit_events`: global hashed auth/security audit events live only in the
dedicated `AUDIT_DB` database. All persisted writes, admin reads, insights,
and auth-denial alerts use that binding; the hourly retention lane prunes rows
after 180 days. Audit events are not user-owned and remain independent of
account deletion/export.
- `stripe_webhook_events`: platform Stripe webhook idempotency rows keep 30 days
by `processed_at`. They are not user-owned and remain independent of account
deletion/export.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS audit_events;
1 change: 0 additions & 1 deletion packages/worker/src/app/account-retention-dispositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const accountRetentionDispositions: ReadonlyArray<AccountRetentionDisposi
{ table: 'email_threads', kind: 'scheduled_policy' },
{ table: 'usage_rollups', kind: 'scheduled_policy' },
{ table: 'feature_flag_exposure_rollups', kind: 'scheduled_policy' },
{ table: 'audit_events', kind: 'scheduled_policy' },
{ table: 'stripe_webhook_events', kind: 'scheduled_policy' },
{
table: 'archived_job_artifacts',
Expand Down
3 changes: 0 additions & 3 deletions packages/worker/src/app/retention.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1232,8 +1232,6 @@ test('retention coverage includes every live growth-pattern table or documented
columnNames.has('user_id') &&
growthTimeColumns.some((column) => columnNames.has(column)) &&
growthPattern.test(table.name)
const hasGlobalAuditShape =
table.name === 'audit_events' && columnNames.has('timestamp')
const hasGlobalStripeWebhookShape =
table.name === 'stripe_webhook_events' && columnNames.has('processed_at')
const hasPlatformFeedbackGrowthShape =
Expand All @@ -1242,7 +1240,6 @@ test('retention coverage includes every live growth-pattern table or documented
columnNames.has('updated_at')
if (
hasUserCreatedGrowthShape ||
hasGlobalAuditShape ||
hasGlobalStripeWebhookShape ||
hasPlatformFeedbackGrowthShape
) {
Expand Down
8 changes: 0 additions & 8 deletions packages/worker/src/app/retention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,6 @@ export const retentionPolicies: ReadonlyArray<RetentionPolicy> = [
description:
'Local-dev/test flag exposure rollups keep 90 days, matching Analytics Engine retention for the production exposure stream; the metric readout window is the current month.',
},
{
table: 'audit_events',
scope: 'global',
retentionDays: auditEventRetentionDays,
batchSize: retentionDefaultBatchSize,
description:
'Global hashed auth/security audit events keep 180 days and are independent of account deletion.',
},
{
table: 'stripe_webhook_events',
scope: 'global',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,17 @@ const emptyDeleteResult = {
}

function createAdminCtx() {
const sqlite = new DatabaseSync(':memory:')
sqlite.exec(`
const appSqlite = new DatabaseSync(':memory:')
appSqlite.exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY,
stable_user_id TEXT UNIQUE NOT NULL,
username TEXT NOT NULL,
email TEXT NOT NULL
);
`)
const auditSqlite = new DatabaseSync(':memory:')
auditSqlite.exec(`
CREATE TABLE audit_events (
id INTEGER PRIMARY KEY,
category TEXT NOT NULL,
Expand All @@ -130,9 +133,11 @@ function createAdminCtx() {
timestamp TEXT NOT NULL
);
`)
const db = createD1FromSqlite(sqlite)
return {
env: { APP_DB: db } as Env,
env: {
APP_DB: createD1FromSqlite(appSqlite),
AUDIT_DB: createD1FromSqlite(auditSqlite),
} as Env,
callerContext: createMcpCallerContext({
baseUrl: 'https://heykody.dev',
user: {
Expand Down
21 changes: 2 additions & 19 deletions packages/worker/src/package-registry/test-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { ensureUsersTestSchema } from '#worker/users-test-schema.ts'
/**
* Schema for package scope grant / platform account workers-unit tests.
* Local D1 does not apply migrations, so suites provision the tables they need.
* Adds migration 0072 (`users.account_type`, `package_scope_grants`) and the
* audit rows those mutations write on top of the shared `users` schema.
* Adds migration 0072 (`users.account_type`, `package_scope_grants`) on top of
* the shared `users` schema.
*/
export async function ensurePackageScopeGrantsTestSchema(db: D1Database) {
await ensureUsersTestSchema({
Expand All @@ -30,21 +30,4 @@ export async function ensurePackageScopeGrantsTestSchema(db: D1Database) {
ON package_scope_grants(grantee_user_id)`,
)
.run()
// Delegated scope resolution and admin grant mutations write audit rows.
await db
.prepare(
`CREATE TABLE IF NOT EXISTS audit_events (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
category TEXT NOT NULL CHECK (category IN ('account', 'admin', 'auth', 'oauth')),
action TEXT NOT NULL,
result TEXT NOT NULL CHECK (result IN ('success', 'failure', 'rate_limited')),
email_hash TEXT,
ip_hash TEXT,
client_id TEXT,
path TEXT,
reason TEXT,
timestamp TEXT NOT NULL
)`,
)
.run()
}
4 changes: 4 additions & 0 deletions tools/migration-ledger.json
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,10 @@
{
"filename": "0126-drop-entitlement-daily-counters.sql",
"sha256": "1fd413a77395d0dd183533863fe381ee5c006e75c8cf4224a4f00c1a57496e26"
},
{
"filename": "0127-drop-legacy-audit-events.sql",
"sha256": "414990ca0d5bf7a1c8cf9d06cffbf46b5e6c3f7cf2dcbfccb00eeca0fa067d70"
}
]
}
Loading