From 80a05bead8b191b7e7bd3f7a23c2450728f14991 Mon Sep 17 00:00:00 2001 From: GautamTalksDev Date: Sat, 29 Aug 2026 19:34:40 -0400 Subject: [PATCH 1/3] test(audit): regression test for fork under identical recorded_at 20 concurrent chained appends forced to share a timestamp; asserts no duplicate prevHash and full chain verification. Fails against the pre-seq implementation. --- .../server/src/db/audit-append-only.test.ts | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/server/src/db/audit-append-only.test.ts b/packages/server/src/db/audit-append-only.test.ts index b0019a4..260f321 100644 --- a/packages/server/src/db/audit-append-only.test.ts +++ b/packages/server/src/db/audit-append-only.test.ts @@ -2,7 +2,7 @@ import { appendAuditRecord, asApprovalCardId, } from "@keyring/core"; -import { sql } from "drizzle-orm"; +import { inArray, sql } from "drizzle-orm"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import type { AppDb } from "./client.js"; @@ -110,4 +110,50 @@ describe("audit_records append-only", () => { expect(after.ok).toBe(true); expect(after.count).toBe(before.count + 10); }); + + it("prevents forks when concurrent appends share an identical recorded_at", async () => { + const recordedAt = "2026-08-29T23:00:00.000Z"; + await db.execute( + sql`ALTER TABLE audit_records ALTER COLUMN recorded_at SET DEFAULT '2026-08-29T23:00:00.000Z'::timestamptz`, + ); + + const appended = await Promise.all( + Array.from({ length: 20 }, (_, i) => + appendChainedAudit(db, { + cardId: asApprovalCardId( + `card-identical-recorded-at-${i}-${crypto.randomUUID()}`, + ), + action: "approve", + approvedBy: "judge@acme.com", + approvedAt: new Date(recordedAt), + executedAt: new Date(recordedAt), + result: "success", + evidenceSnapshot: evidence, + }), + ), + ); + + const appendedRows = await db + .select({ + id: auditRecords.id, + recordedAt: auditRecords.recordedAt, + }) + .from(auditRecords) + .where( + inArray( + auditRecords.id, + appended.map((record) => record.id), + ), + ); + expect(new Set(appendedRows.map((row) => row.recordedAt.toISOString())).size).toBe( + 1, + ); + + const allRows = await db + .select({ prevHash: auditRecords.prevHash }) + .from(auditRecords); + const prevHashes = allRows.map((row) => row.prevHash); + expect(new Set(prevHashes).size).toBe(prevHashes.length); + expect((await verifyStoredAuditChain(db)).ok).toBe(true); + }); }); From 8584e27137fbeb34deff2e98fb17ac494b2a075a Mon Sep 17 00:00:00 2001 From: GautamTalksDev Date: Sat, 29 Aug 2026 19:39:30 -0400 Subject: [PATCH 2/3] fix(ci): build workspace dependencies before typechecking Use TypeScript project references so clean runners compile core before dependent packages, and fix the connector strict-mode parameter types. Co-authored-by: Cursor --- package.json | 4 ++-- packages/connectors/package.json | 4 ++-- packages/connectors/src/github/connector.ts | 6 ++++-- packages/connectors/src/google-workspace/connector.ts | 5 +++-- packages/connectors/tsconfig.json | 4 +++- packages/core/package.json | 4 ++-- packages/core/tsconfig.json | 1 + packages/server/package.json | 4 ++-- packages/server/tsconfig.json | 4 +++- tsconfig.json | 8 ++++++++ 10 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 tsconfig.json diff --git a/package.json b/package.json index 8c39dc6..683ac69 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,12 @@ "scripts": { "demo": "tsx scripts/demo.ts", "dev": "pnpm --parallel --filter @keyring/server --filter @keyring/web dev", - "build": "pnpm -r run build", + "build": "tsc -b --pretty false && pnpm --filter @keyring/web run build", "test": "vitest run", "test:watch": "vitest", "lint": "eslint .", "lint:fix": "eslint . --fix", - "typecheck": "pnpm -r run typecheck", + "typecheck": "tsc -b --pretty false && pnpm --filter @keyring/web exec tsc -p tsconfig.json --noEmit", "format": "prettier --write .", "format:check": "prettier --check .", "db:migrate": "pnpm --filter @keyring/server db:migrate", diff --git a/packages/connectors/package.json b/packages/connectors/package.json index 7d5cf6f..dddbd57 100644 --- a/packages/connectors/package.json +++ b/packages/connectors/package.json @@ -13,8 +13,8 @@ "types": "./dist/index.d.ts", "files": ["dist", "fixtures"], "scripts": { - "build": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig.json --noEmit" + "build": "tsc -b tsconfig.json --pretty false", + "typecheck": "tsc -b tsconfig.json --pretty false" }, "dependencies": { "@keyring/core": "workspace:*" diff --git a/packages/connectors/src/github/connector.ts b/packages/connectors/src/github/connector.ts index be71181..93a8777 100644 --- a/packages/connectors/src/github/connector.ts +++ b/packages/connectors/src/github/connector.ts @@ -432,12 +432,14 @@ export function createGitHubConnector(options: GitHubConnectorOptions): Connecto return { ok: false, error: "not a github grant" }; } - const login = grant.principal.identifiers.find((i) => i.kind === "username")?.value; + const login = grant.principal.identifiers.find( + (i: Identifier) => i.kind === "username", + )?.value; const resourceId = String(grant.resource.id); const teamMatch = resourceId.match(/^([^/]+)\/team:(.+)$/); const patMatch = resourceId.match(/^([^/]+)\/pat:(.+)$/); const deployKeyId = grant.principal.identifiers.find( - (i) => i.kind === "key_id" && i.source === "github_deploy_keys", + (i: Identifier) => i.kind === "key_id" && i.source === "github_deploy_keys", )?.value; // --- Team membership --- diff --git a/packages/connectors/src/google-workspace/connector.ts b/packages/connectors/src/google-workspace/connector.ts index ec795c9..8fbe02e 100644 --- a/packages/connectors/src/google-workspace/connector.ts +++ b/packages/connectors/src/google-workspace/connector.ts @@ -1,4 +1,4 @@ -import { createGrant, type Grant } from "@keyring/core"; +import { createGrant, type Grant, type Identifier } from "@keyring/core"; import { asArray, asObject, callJson, paginateTokens } from "../mcp/paginate.js"; import { McpToolError, type McpToolCaller } from "../mcp/types.js"; @@ -374,7 +374,8 @@ export function createGoogleWorkspaceConnector( return { ok: false, error: "not a google_workspace grant" }; } const email = grant.principal.identifiers.find( - (i) => i.kind === "work_email" || i.kind === "personal_email", + (i: Identifier) => + i.kind === "work_email" || i.kind === "personal_email", )?.value; if (!email) return { ok: false, error: "missing email identifier" }; diff --git a/packages/connectors/tsconfig.json b/packages/connectors/tsconfig.json index 7b02c75..4cf982e 100644 --- a/packages/connectors/tsconfig.json +++ b/packages/connectors/tsconfig.json @@ -1,9 +1,11 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "composite": true, "outDir": "dist", "rootDir": "src" }, "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "exclude": ["src/**/*.test.ts"], + "references": [{ "path": "../core" }] } diff --git a/packages/core/package.json b/packages/core/package.json index 5e275cd..244dde7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -13,8 +13,8 @@ "types": "./dist/index.d.ts", "files": ["dist"], "scripts": { - "build": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig.json --noEmit", + "build": "tsc -b tsconfig.json --pretty false", + "typecheck": "tsc -b tsconfig.json --pretty false", "reconcile": "node dist/identity/cli.js" }, "bin": { diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 7b02c75..9889060 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "composite": true, "outDir": "dist", "rootDir": "src" }, diff --git a/packages/server/package.json b/packages/server/package.json index 8e01843..6a992a1 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -21,8 +21,8 @@ ], "scripts": { "dev": "tsx watch src/index.ts", - "build": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig.json --noEmit", + "build": "tsc -b tsconfig.json --pretty false", + "typecheck": "tsc -b tsconfig.json --pretty false", "start": "node dist/index.js", "db:generate": "drizzle-kit generate", "db:migrate": "tsx src/db/migrate-cli.ts" diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index 7b02c75..4f1515e 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -1,9 +1,11 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "composite": true, "outDir": "dist", "rootDir": "src" }, "include": ["src/**/*"], - "exclude": ["src/**/*.test.ts"] + "exclude": ["src/**/*.test.ts"], + "references": [{ "path": "../core" }, { "path": "../connectors" }] } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2b884fa --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "files": [], + "references": [ + { "path": "./packages/core" }, + { "path": "./packages/connectors" }, + { "path": "./packages/server" } + ] +} From 361e33eae63555bf0e8bcce1b5e75c0bc232883d Mon Sep 17 00:00:00 2001 From: GautamTalksDev Date: Sat, 29 Aug 2026 19:45:42 -0400 Subject: [PATCH 3/3] test(audit): restore recorded_at default in finally block Addresses Qodo review: the fixed default leaked into subsequent tests on the shared Postgres backend and was never restored on assertion failure. Co-authored-by: Cursor --- .../server/src/db/audit-append-only.test.ts | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/packages/server/src/db/audit-append-only.test.ts b/packages/server/src/db/audit-append-only.test.ts index 260f321..cc6dcc7 100644 --- a/packages/server/src/db/audit-append-only.test.ts +++ b/packages/server/src/db/audit-append-only.test.ts @@ -111,49 +111,57 @@ describe("audit_records append-only", () => { expect(after.count).toBe(before.count + 10); }); - it("prevents forks when concurrent appends share an identical recorded_at", async () => { + it("restores the timestamp default after concurrent identical-time appends", async () => { const recordedAt = "2026-08-29T23:00:00.000Z"; - await db.execute( - sql`ALTER TABLE audit_records ALTER COLUMN recorded_at SET DEFAULT '2026-08-29T23:00:00.000Z'::timestamptz`, - ); - const appended = await Promise.all( - Array.from({ length: 20 }, (_, i) => - appendChainedAudit(db, { - cardId: asApprovalCardId( - `card-identical-recorded-at-${i}-${crypto.randomUUID()}`, - ), - action: "approve", - approvedBy: "judge@acme.com", - approvedAt: new Date(recordedAt), - executedAt: new Date(recordedAt), - result: "success", - evidenceSnapshot: evidence, - }), - ), - ); + try { + await db.execute( + sql`ALTER TABLE audit_records ALTER COLUMN recorded_at SET DEFAULT '2026-08-29T23:00:00.000Z'::timestamptz`, + ); - const appendedRows = await db - .select({ - id: auditRecords.id, - recordedAt: auditRecords.recordedAt, - }) - .from(auditRecords) - .where( - inArray( - auditRecords.id, - appended.map((record) => record.id), + const appended = await Promise.all( + Array.from({ length: 20 }, (_, i) => + appendChainedAudit(db, { + cardId: asApprovalCardId( + `card-identical-recorded-at-${i}-${crypto.randomUUID()}`, + ), + action: "approve", + approvedBy: "judge@acme.com", + approvedAt: new Date(recordedAt), + executedAt: new Date(recordedAt), + result: "success", + evidenceSnapshot: evidence, + }), ), ); - expect(new Set(appendedRows.map((row) => row.recordedAt.toISOString())).size).toBe( - 1, - ); - const allRows = await db - .select({ prevHash: auditRecords.prevHash }) - .from(auditRecords); - const prevHashes = allRows.map((row) => row.prevHash); - expect(new Set(prevHashes).size).toBe(prevHashes.length); - expect((await verifyStoredAuditChain(db)).ok).toBe(true); + const appendedRows = await db + .select({ + id: auditRecords.id, + recordedAt: auditRecords.recordedAt, + }) + .from(auditRecords) + .where( + inArray( + auditRecords.id, + appended.map((record) => record.id), + ), + ); + expect(appendedRows).toHaveLength(20); + expect( + new Set(appendedRows.map((row) => row.recordedAt.toISOString())), + ).toEqual(new Set([recordedAt])); + + const allRows = await db + .select({ prevHash: auditRecords.prevHash }) + .from(auditRecords); + const prevHashes = allRows.map((row) => row.prevHash); + expect(new Set(prevHashes).size).toBe(prevHashes.length); + expect((await verifyStoredAuditChain(db)).ok).toBe(true); + } finally { + await db.execute( + sql`ALTER TABLE audit_records ALTER COLUMN recorded_at SET DEFAULT now()`, + ); + } }); });