@minion-stack/db 0.2.0 → 0.3.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.
Files changed (57) hide show
  1. package/dist/pg/crypto.d.ts +8 -0
  2. package/dist/pg/crypto.d.ts.map +1 -0
  3. package/dist/pg/crypto.js +42 -0
  4. package/dist/pg/crypto.js.map +1 -0
  5. package/dist/pg/crypto.test.d.ts +2 -0
  6. package/dist/pg/crypto.test.d.ts.map +1 -0
  7. package/dist/pg/crypto.test.js +25 -0
  8. package/dist/pg/crypto.test.js.map +1 -0
  9. package/dist/pg/identity-mapper.d.ts +36 -0
  10. package/dist/pg/identity-mapper.d.ts.map +1 -0
  11. package/dist/pg/identity-mapper.js +18 -0
  12. package/dist/pg/identity-mapper.js.map +1 -0
  13. package/dist/pg/identity-mapper.test.d.ts +2 -0
  14. package/dist/pg/identity-mapper.test.d.ts.map +1 -0
  15. package/dist/pg/identity-mapper.test.js +32 -0
  16. package/dist/pg/identity-mapper.test.js.map +1 -0
  17. package/dist/pg/schema/index.d.ts +6 -0
  18. package/dist/pg/schema/index.d.ts.map +1 -0
  19. package/dist/pg/schema/index.js +6 -0
  20. package/dist/pg/schema/index.js.map +1 -0
  21. package/dist/pg/schema/profiles.d.ts +134 -0
  22. package/dist/pg/schema/profiles.d.ts.map +1 -0
  23. package/dist/pg/schema/profiles.js +20 -0
  24. package/dist/pg/schema/profiles.js.map +1 -0
  25. package/dist/pg/schema/user-identities.d.ts +237 -0
  26. package/dist/pg/schema/user-identities.d.ts.map +1 -0
  27. package/dist/pg/schema/user-identities.js +31 -0
  28. package/dist/pg/schema/user-identities.js.map +1 -0
  29. package/dist/schema/index.d.ts +3 -0
  30. package/dist/schema/index.d.ts.map +1 -1
  31. package/dist/schema/index.js +2 -0
  32. package/dist/schema/index.js.map +1 -1
  33. package/dist/schema/personal-agents.d.ts +1 -1
  34. package/dist/schema/reliability-events.d.ts +1 -1
  35. package/dist/schema/server-provision-configs.d.ts +1 -1
  36. package/dist/schema/session-tasks.d.ts +1 -1
  37. package/dist/schema/skill-execution-stats.d.ts +1 -1
  38. package/dist/schema/tasks.d.ts +1 -1
  39. package/dist/schema/user-identities.d.ts +254 -0
  40. package/dist/schema/user-identities.d.ts.map +1 -0
  41. package/dist/schema/user-identities.js +30 -0
  42. package/dist/schema/user-identities.js.map +1 -0
  43. package/dist/schema/workspace-membership.d.ts +94 -0
  44. package/dist/schema/workspace-membership.d.ts.map +1 -0
  45. package/dist/schema/workspace-membership.js +24 -0
  46. package/dist/schema/workspace-membership.js.map +1 -0
  47. package/package.json +18 -9
  48. package/src/pg/crypto.test.ts +28 -0
  49. package/src/pg/crypto.ts +44 -0
  50. package/src/pg/identity-mapper.test.ts +35 -0
  51. package/src/pg/identity-mapper.ts +48 -0
  52. package/src/pg/schema/index.ts +12 -0
  53. package/src/pg/schema/profiles.ts +20 -0
  54. package/src/pg/schema/user-identities.ts +35 -0
  55. package/src/schema/index.ts +3 -0
  56. package/src/schema/user-identities.ts +34 -0
  57. package/src/schema/workspace-membership.ts +31 -0
@@ -0,0 +1,8 @@
1
+ /** Seal plaintext → { ciphertext, iv }. ciphertext = hex(encrypted || authTag). */
2
+ export declare function sealSecret(plaintext: string): {
3
+ ciphertext: string;
4
+ iv: string;
5
+ };
6
+ /** Open hex(encrypted || authTag) + hex(iv) → plaintext. Throws on auth failure. */
7
+ export declare function openSecret(ciphertext: string, iv: string): string;
8
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/pg/crypto.ts"],"names":[],"mappings":"AAyBA,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAOhF;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAOjE"}
@@ -0,0 +1,42 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
2
+ const ALGORITHM = 'aes-256-gcm';
3
+ const IV_BYTES = 12;
4
+ const AUTH_TAG_BYTES = 16;
5
+ // MUST match minion_hub/src/server/auth/crypto.ts so hub + site interoperate:
6
+ // key = scryptSync(ENCRYPTION_KEY, 'minion-hub-salt', 32)
7
+ // ciphertext (hex) = encrypted || authTag (16-byte tag LAST)
8
+ // iv (hex) = 12 random bytes, stored separately
9
+ let cachedKey = null;
10
+ function key() {
11
+ if (cachedKey)
12
+ return cachedKey;
13
+ const raw = process.env.ENCRYPTION_KEY;
14
+ if (!raw) {
15
+ if (process.env.NODE_ENV === 'production') {
16
+ throw new Error('ENCRYPTION_KEY environment variable must be set in production');
17
+ }
18
+ cachedKey = scryptSync('minion-hub-dev-key', 'minion-hub-salt', 32);
19
+ return cachedKey;
20
+ }
21
+ cachedKey = scryptSync(raw, 'minion-hub-salt', 32);
22
+ return cachedKey;
23
+ }
24
+ /** Seal plaintext → { ciphertext, iv }. ciphertext = hex(encrypted || authTag). */
25
+ export function sealSecret(plaintext) {
26
+ const iv = randomBytes(IV_BYTES);
27
+ const cipher = createCipheriv(ALGORITHM, key(), iv);
28
+ const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
29
+ const authTag = cipher.getAuthTag();
30
+ const combined = Buffer.concat([encrypted, authTag]);
31
+ return { ciphertext: combined.toString('hex'), iv: iv.toString('hex') };
32
+ }
33
+ /** Open hex(encrypted || authTag) + hex(iv) → plaintext. Throws on auth failure. */
34
+ export function openSecret(ciphertext, iv) {
35
+ const combined = Buffer.from(ciphertext, 'hex');
36
+ const encrypted = combined.subarray(0, combined.length - AUTH_TAG_BYTES);
37
+ const authTag = combined.subarray(combined.length - AUTH_TAG_BYTES);
38
+ const decipher = createDecipheriv(ALGORITHM, key(), Buffer.from(iv, 'hex'));
39
+ decipher.setAuthTag(authTag);
40
+ return decipher.update(encrypted) + decipher.final('utf8');
41
+ }
42
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/pg/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAExF,MAAM,SAAS,GAAG,aAAa,CAAC;AAChC,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,8EAA8E;AAC9E,4DAA4D;AAC5D,iEAAiE;AACjE,kDAAkD;AAClD,IAAI,SAAS,GAAkB,IAAI,CAAC;AACpC,SAAS,GAAG;IACV,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;QACD,SAAS,GAAG,UAAU,CAAC,oBAAoB,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;QACpE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;IACnD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,MAAM,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,UAAkB,EAAE,EAAU;IACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=crypto.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.test.d.ts","sourceRoot":"","sources":["../../src/pg/crypto.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import { sealSecret, openSecret } from './crypto.js';
3
+ beforeAll(() => {
4
+ process.env.ENCRYPTION_KEY = 'test-key-do-not-use-in-prod';
5
+ });
6
+ describe('identity secret crypto', () => {
7
+ it('round-trips a plaintext through seal/open', () => {
8
+ const plaintext = JSON.stringify({ type: 'authorized_user', refresh_token: 'abc123' });
9
+ const { ciphertext, iv } = sealSecret(plaintext);
10
+ expect(ciphertext).toMatch(/^[0-9a-f]+$/);
11
+ expect(iv).toMatch(/^[0-9a-f]+$/);
12
+ expect(openSecret(ciphertext, iv)).toBe(plaintext);
13
+ });
14
+ it('fails to open with a tampered ciphertext (GCM auth)', () => {
15
+ const { ciphertext, iv } = sealSecret('hello');
16
+ const tampered = ciphertext.slice(0, -2) + (ciphertext.endsWith('00') ? '11' : '00');
17
+ expect(() => openSecret(tampered, iv)).toThrow();
18
+ });
19
+ it('is wire-compatible with hub layout (ciphertext = hex(encrypted || authTag), 16-byte tag last)', () => {
20
+ const { ciphertext } = sealSecret('x');
21
+ // hex of (>=0 bytes ciphertext) + 16-byte tag → at least 32 hex chars of tag.
22
+ expect(ciphertext.length).toBeGreaterThanOrEqual(32);
23
+ });
24
+ });
25
+ //# sourceMappingURL=crypto.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.test.js","sourceRoot":"","sources":["../../src/pg/crypto.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,SAAS,CAAC,GAAG,EAAE;IACb,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,6BAA6B,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvF,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAClC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrF,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+FAA+F,EAAE,GAAG,EAAE;QACvG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACvC,8EAA8E;QAC9E,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,36 @@
1
+ export interface GoTrueUserLike {
2
+ id: string;
3
+ email: string | null;
4
+ user_metadata?: {
5
+ full_name?: string;
6
+ name?: string;
7
+ provider_id?: string;
8
+ } | null;
9
+ app_metadata?: {
10
+ provider?: string;
11
+ } | null;
12
+ }
13
+ export interface GoogleGrant {
14
+ refreshToken: string | null;
15
+ scope: string | null;
16
+ }
17
+ export interface MappedProfile {
18
+ id: string;
19
+ email: string;
20
+ displayName: string | null;
21
+ }
22
+ export interface MappedIdentity {
23
+ userId: string;
24
+ provider: 'google';
25
+ kind: 'oauth';
26
+ externalId: string;
27
+ displayName: string | null;
28
+ scope: string | null;
29
+ hasSecret: boolean;
30
+ }
31
+ /** Pure mapping — no DB, no crypto. Caller seals the secret and upserts. */
32
+ export declare function mapGoogleIdentity(user: GoTrueUserLike, grant: GoogleGrant): {
33
+ profile: MappedProfile;
34
+ identity: MappedIdentity;
35
+ };
36
+ //# sourceMappingURL=identity-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-mapper.d.ts","sourceRoot":"","sources":["../../src/pg/identity-mapper.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,WAAW,GACjB;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,CAetD"}
@@ -0,0 +1,18 @@
1
+ /** Pure mapping — no DB, no crypto. Caller seals the secret and upserts. */
2
+ export function mapGoogleIdentity(user, grant) {
3
+ const email = user.email ?? '';
4
+ const displayName = user.user_metadata?.full_name ?? user.user_metadata?.name ?? null;
5
+ return {
6
+ profile: { id: user.id, email, displayName },
7
+ identity: {
8
+ userId: user.id,
9
+ provider: 'google',
10
+ kind: 'oauth',
11
+ externalId: email,
12
+ displayName,
13
+ scope: grant.scope,
14
+ hasSecret: Boolean(grant.refreshToken),
15
+ },
16
+ };
17
+ }
18
+ //# sourceMappingURL=identity-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-mapper.js","sourceRoot":"","sources":["../../src/pg/identity-mapper.ts"],"names":[],"mappings":"AA4BA,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAC/B,IAAoB,EACpB,KAAkB;IAElB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,IAAI,IAAI,IAAI,CAAC;IACtF,OAAO;QACL,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;QAC5C,QAAQ,EAAE;YACR,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,OAAO;YACb,UAAU,EAAE,KAAK;YACjB,WAAW;YACX,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC;SACvC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=identity-mapper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-mapper.test.d.ts","sourceRoot":"","sources":["../../src/pg/identity-mapper.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mapGoogleIdentity } from './identity-mapper.js';
3
+ const gotrueUser = {
4
+ id: '11111111-1111-1111-1111-111111111111',
5
+ email: 'nik@example.com',
6
+ user_metadata: { full_name: 'Nik P', provider_id: 'google-sub-123' },
7
+ app_metadata: { provider: 'google' },
8
+ };
9
+ describe('mapGoogleIdentity', () => {
10
+ it('produces a profile row from the GoTrue user', () => {
11
+ const { profile } = mapGoogleIdentity(gotrueUser, { refreshToken: 'rt', scope: 'email profile' });
12
+ expect(profile).toEqual({
13
+ id: '11111111-1111-1111-1111-111111111111',
14
+ email: 'nik@example.com',
15
+ displayName: 'Nik P',
16
+ });
17
+ });
18
+ it('produces a google oauth identity keyed by email as externalId', () => {
19
+ const { identity } = mapGoogleIdentity(gotrueUser, { refreshToken: 'rt', scope: 'email profile' });
20
+ expect(identity.provider).toBe('google');
21
+ expect(identity.kind).toBe('oauth');
22
+ expect(identity.externalId).toBe('nik@example.com');
23
+ expect(identity.userId).toBe(gotrueUser.id);
24
+ expect(identity.scope).toBe('email profile');
25
+ expect(identity.hasSecret).toBe(true);
26
+ });
27
+ it('marks hasSecret false when no refresh token present', () => {
28
+ const { identity } = mapGoogleIdentity(gotrueUser, { refreshToken: null, scope: null });
29
+ expect(identity.hasSecret).toBe(false);
30
+ });
31
+ });
32
+ //# sourceMappingURL=identity-mapper.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-mapper.test.js","sourceRoot":"","sources":["../../src/pg/identity-mapper.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAuB,MAAM,sBAAsB,CAAC;AAE9E,MAAM,UAAU,GAAmB;IACjC,EAAE,EAAE,sCAAsC;IAC1C,KAAK,EAAE,iBAAiB;IACxB,aAAa,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE;IACpE,YAAY,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;CACrC,CAAC;AAEF,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,UAAU,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;QAClG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YACtB,EAAE,EAAE,sCAAsC;YAC1C,KAAK,EAAE,iBAAiB;YACxB,WAAW,EAAE,OAAO;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CAAC,UAAU,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACpD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CAAC,UAAU,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { profiles } from './profiles.js';
2
+ export { userIdentities } from './user-identities.js';
3
+ export { mapGoogleIdentity } from '../identity-mapper.js';
4
+ export type { GoTrueUserLike, GoogleGrant, MappedProfile, MappedIdentity, } from '../identity-mapper.js';
5
+ export { sealSecret, openSecret } from '../crypto.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pg/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { profiles } from './profiles.js';
2
+ export { userIdentities } from './user-identities.js';
3
+ // Identity helpers (consumed by minion_site auth path)
4
+ export { mapGoogleIdentity } from '../identity-mapper.js';
5
+ export { sealSecret, openSecret } from '../crypto.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/pg/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,uDAAuD;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAO1D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Canonical app-level user. 1:1 with GoTrue `auth.users` (id is the same uuid).
3
+ * GoTrue owns auth.users; this row is created post-signup by identity-sync.
4
+ * We intentionally do NOT add a Drizzle FK to auth.users (auth schema is
5
+ * GoTrue-managed); the FK is declared in the hand-written RLS/constraints
6
+ * migration instead.
7
+ */
8
+ export declare const profiles: import("drizzle-orm/pg-core").PgTableWithColumns<{
9
+ name: "profiles";
10
+ schema: undefined;
11
+ columns: {
12
+ id: import("drizzle-orm/pg-core").PgColumn<{
13
+ name: "id";
14
+ tableName: "profiles";
15
+ dataType: "string";
16
+ columnType: "PgUUID";
17
+ data: string;
18
+ driverParam: string;
19
+ notNull: true;
20
+ hasDefault: false;
21
+ isPrimaryKey: true;
22
+ isAutoincrement: false;
23
+ hasRuntimeDefault: false;
24
+ enumValues: undefined;
25
+ baseColumn: never;
26
+ identity: undefined;
27
+ generated: undefined;
28
+ }, {}, {}>;
29
+ email: import("drizzle-orm/pg-core").PgColumn<{
30
+ name: "email";
31
+ tableName: "profiles";
32
+ dataType: "string";
33
+ columnType: "PgText";
34
+ data: string;
35
+ driverParam: string;
36
+ notNull: true;
37
+ hasDefault: false;
38
+ isPrimaryKey: false;
39
+ isAutoincrement: false;
40
+ hasRuntimeDefault: false;
41
+ enumValues: [string, ...string[]];
42
+ baseColumn: never;
43
+ identity: undefined;
44
+ generated: undefined;
45
+ }, {}, {}>;
46
+ displayName: import("drizzle-orm/pg-core").PgColumn<{
47
+ name: "display_name";
48
+ tableName: "profiles";
49
+ dataType: "string";
50
+ columnType: "PgText";
51
+ data: string;
52
+ driverParam: string;
53
+ notNull: false;
54
+ hasDefault: false;
55
+ isPrimaryKey: false;
56
+ isAutoincrement: false;
57
+ hasRuntimeDefault: false;
58
+ enumValues: [string, ...string[]];
59
+ baseColumn: never;
60
+ identity: undefined;
61
+ generated: undefined;
62
+ }, {}, {}>;
63
+ role: import("drizzle-orm/pg-core").PgColumn<{
64
+ name: "role";
65
+ tableName: "profiles";
66
+ dataType: "string";
67
+ columnType: "PgText";
68
+ data: "user" | "admin";
69
+ driverParam: string;
70
+ notNull: true;
71
+ hasDefault: true;
72
+ isPrimaryKey: false;
73
+ isAutoincrement: false;
74
+ hasRuntimeDefault: false;
75
+ enumValues: ["user", "admin"];
76
+ baseColumn: never;
77
+ identity: undefined;
78
+ generated: undefined;
79
+ }, {}, {}>;
80
+ personalAgentId: import("drizzle-orm/pg-core").PgColumn<{
81
+ name: "personal_agent_id";
82
+ tableName: "profiles";
83
+ dataType: "string";
84
+ columnType: "PgText";
85
+ data: string;
86
+ driverParam: string;
87
+ notNull: false;
88
+ hasDefault: false;
89
+ isPrimaryKey: false;
90
+ isAutoincrement: false;
91
+ hasRuntimeDefault: false;
92
+ enumValues: [string, ...string[]];
93
+ baseColumn: never;
94
+ identity: undefined;
95
+ generated: undefined;
96
+ }, {}, {}>;
97
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
98
+ name: "created_at";
99
+ tableName: "profiles";
100
+ dataType: "date";
101
+ columnType: "PgTimestamp";
102
+ data: Date;
103
+ driverParam: string;
104
+ notNull: true;
105
+ hasDefault: true;
106
+ isPrimaryKey: false;
107
+ isAutoincrement: false;
108
+ hasRuntimeDefault: false;
109
+ enumValues: undefined;
110
+ baseColumn: never;
111
+ identity: undefined;
112
+ generated: undefined;
113
+ }, {}, {}>;
114
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
115
+ name: "updated_at";
116
+ tableName: "profiles";
117
+ dataType: "date";
118
+ columnType: "PgTimestamp";
119
+ data: Date;
120
+ driverParam: string;
121
+ notNull: true;
122
+ hasDefault: true;
123
+ isPrimaryKey: false;
124
+ isAutoincrement: false;
125
+ hasRuntimeDefault: false;
126
+ enumValues: undefined;
127
+ baseColumn: never;
128
+ identity: undefined;
129
+ generated: undefined;
130
+ }, {}, {}>;
131
+ };
132
+ dialect: "pg";
133
+ }>;
134
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../../src/pg/schema/profiles.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUnB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core';
2
+ /**
3
+ * Canonical app-level user. 1:1 with GoTrue `auth.users` (id is the same uuid).
4
+ * GoTrue owns auth.users; this row is created post-signup by identity-sync.
5
+ * We intentionally do NOT add a Drizzle FK to auth.users (auth schema is
6
+ * GoTrue-managed); the FK is declared in the hand-written RLS/constraints
7
+ * migration instead.
8
+ */
9
+ export const profiles = pgTable('profiles', {
10
+ id: uuid('id').primaryKey(), // == auth.users.id
11
+ email: text('email').notNull(),
12
+ displayName: text('display_name'),
13
+ role: text('role', { enum: ['user', 'admin'] })
14
+ .notNull()
15
+ .default('user'),
16
+ personalAgentId: text('personal_agent_id'),
17
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
18
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
19
+ });
20
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/pg/schema/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE;IAC1C,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,mBAAmB;IAChD,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IAC9B,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC;IACjC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;SAC5C,OAAO,EAAE;SACT,OAAO,CAAC,MAAM,CAAC;IAClB,eAAe,EAAE,IAAI,CAAC,mBAAmB,CAAC;IAC1C,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;IACjF,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CAClF,CAAC,CAAC"}
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Canonical per-user identity row linking a user to BOTH OAuth providers
3
+ * (kind='oauth', e.g. google) AND channel identities (kind='channel',
4
+ * e.g. whatsapp/telegram/discord/signal/slack). This is the single link
5
+ * table replacing the SQLite channel_identities + the oauth side of account.
6
+ * Secret material (OAuth refresh-token ADC blob) is app-level AES-256-GCM
7
+ * sealed into secretCiphertext/secretIv; null for channel identities.
8
+ */
9
+ export declare const userIdentities: import("drizzle-orm/pg-core").PgTableWithColumns<{
10
+ name: "user_identities";
11
+ schema: undefined;
12
+ columns: {
13
+ id: import("drizzle-orm/pg-core").PgColumn<{
14
+ name: "id";
15
+ tableName: "user_identities";
16
+ dataType: "string";
17
+ columnType: "PgUUID";
18
+ data: string;
19
+ driverParam: string;
20
+ notNull: true;
21
+ hasDefault: true;
22
+ isPrimaryKey: true;
23
+ isAutoincrement: false;
24
+ hasRuntimeDefault: false;
25
+ enumValues: undefined;
26
+ baseColumn: never;
27
+ identity: undefined;
28
+ generated: undefined;
29
+ }, {}, {}>;
30
+ userId: import("drizzle-orm/pg-core").PgColumn<{
31
+ name: "user_id";
32
+ tableName: "user_identities";
33
+ dataType: "string";
34
+ columnType: "PgUUID";
35
+ data: string;
36
+ driverParam: string;
37
+ notNull: true;
38
+ hasDefault: false;
39
+ isPrimaryKey: false;
40
+ isAutoincrement: false;
41
+ hasRuntimeDefault: false;
42
+ enumValues: undefined;
43
+ baseColumn: never;
44
+ identity: undefined;
45
+ generated: undefined;
46
+ }, {}, {}>;
47
+ provider: import("drizzle-orm/pg-core").PgColumn<{
48
+ name: "provider";
49
+ tableName: "user_identities";
50
+ dataType: "string";
51
+ columnType: "PgText";
52
+ data: string;
53
+ driverParam: string;
54
+ notNull: true;
55
+ hasDefault: false;
56
+ isPrimaryKey: false;
57
+ isAutoincrement: false;
58
+ hasRuntimeDefault: false;
59
+ enumValues: [string, ...string[]];
60
+ baseColumn: never;
61
+ identity: undefined;
62
+ generated: undefined;
63
+ }, {}, {}>;
64
+ kind: import("drizzle-orm/pg-core").PgColumn<{
65
+ name: "kind";
66
+ tableName: "user_identities";
67
+ dataType: "string";
68
+ columnType: "PgText";
69
+ data: "oauth" | "channel";
70
+ driverParam: string;
71
+ notNull: true;
72
+ hasDefault: false;
73
+ isPrimaryKey: false;
74
+ isAutoincrement: false;
75
+ hasRuntimeDefault: false;
76
+ enumValues: ["oauth", "channel"];
77
+ baseColumn: never;
78
+ identity: undefined;
79
+ generated: undefined;
80
+ }, {}, {}>;
81
+ externalId: import("drizzle-orm/pg-core").PgColumn<{
82
+ name: "external_id";
83
+ tableName: "user_identities";
84
+ dataType: "string";
85
+ columnType: "PgText";
86
+ data: string;
87
+ driverParam: string;
88
+ notNull: true;
89
+ hasDefault: false;
90
+ isPrimaryKey: false;
91
+ isAutoincrement: false;
92
+ hasRuntimeDefault: false;
93
+ enumValues: [string, ...string[]];
94
+ baseColumn: never;
95
+ identity: undefined;
96
+ generated: undefined;
97
+ }, {}, {}>;
98
+ displayName: import("drizzle-orm/pg-core").PgColumn<{
99
+ name: "display_name";
100
+ tableName: "user_identities";
101
+ dataType: "string";
102
+ columnType: "PgText";
103
+ data: string;
104
+ driverParam: string;
105
+ notNull: false;
106
+ hasDefault: false;
107
+ isPrimaryKey: false;
108
+ isAutoincrement: false;
109
+ hasRuntimeDefault: false;
110
+ enumValues: [string, ...string[]];
111
+ baseColumn: never;
112
+ identity: undefined;
113
+ generated: undefined;
114
+ }, {}, {}>;
115
+ scope: import("drizzle-orm/pg-core").PgColumn<{
116
+ name: "scope";
117
+ tableName: "user_identities";
118
+ dataType: "string";
119
+ columnType: "PgText";
120
+ data: string;
121
+ driverParam: string;
122
+ notNull: false;
123
+ hasDefault: false;
124
+ isPrimaryKey: false;
125
+ isAutoincrement: false;
126
+ hasRuntimeDefault: false;
127
+ enumValues: [string, ...string[]];
128
+ baseColumn: never;
129
+ identity: undefined;
130
+ generated: undefined;
131
+ }, {}, {}>;
132
+ secretCiphertext: import("drizzle-orm/pg-core").PgColumn<{
133
+ name: "secret_ciphertext";
134
+ tableName: "user_identities";
135
+ dataType: "string";
136
+ columnType: "PgText";
137
+ data: string;
138
+ driverParam: string;
139
+ notNull: false;
140
+ hasDefault: false;
141
+ isPrimaryKey: false;
142
+ isAutoincrement: false;
143
+ hasRuntimeDefault: false;
144
+ enumValues: [string, ...string[]];
145
+ baseColumn: never;
146
+ identity: undefined;
147
+ generated: undefined;
148
+ }, {}, {}>;
149
+ secretIv: import("drizzle-orm/pg-core").PgColumn<{
150
+ name: "secret_iv";
151
+ tableName: "user_identities";
152
+ dataType: "string";
153
+ columnType: "PgText";
154
+ data: string;
155
+ driverParam: string;
156
+ notNull: false;
157
+ hasDefault: false;
158
+ isPrimaryKey: false;
159
+ isAutoincrement: false;
160
+ hasRuntimeDefault: false;
161
+ enumValues: [string, ...string[]];
162
+ baseColumn: never;
163
+ identity: undefined;
164
+ generated: undefined;
165
+ }, {}, {}>;
166
+ expiresAt: import("drizzle-orm/pg-core").PgColumn<{
167
+ name: "expires_at";
168
+ tableName: "user_identities";
169
+ dataType: "number";
170
+ columnType: "PgBigInt53";
171
+ data: number;
172
+ driverParam: string | number;
173
+ notNull: false;
174
+ hasDefault: false;
175
+ isPrimaryKey: false;
176
+ isAutoincrement: false;
177
+ hasRuntimeDefault: false;
178
+ enumValues: undefined;
179
+ baseColumn: never;
180
+ identity: undefined;
181
+ generated: undefined;
182
+ }, {}, {}>;
183
+ verifiedAt: import("drizzle-orm/pg-core").PgColumn<{
184
+ name: "verified_at";
185
+ tableName: "user_identities";
186
+ dataType: "number";
187
+ columnType: "PgBigInt53";
188
+ data: number;
189
+ driverParam: string | number;
190
+ notNull: false;
191
+ hasDefault: false;
192
+ isPrimaryKey: false;
193
+ isAutoincrement: false;
194
+ hasRuntimeDefault: false;
195
+ enumValues: undefined;
196
+ baseColumn: never;
197
+ identity: undefined;
198
+ generated: undefined;
199
+ }, {}, {}>;
200
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
201
+ name: "created_at";
202
+ tableName: "user_identities";
203
+ dataType: "date";
204
+ columnType: "PgTimestamp";
205
+ data: Date;
206
+ driverParam: string;
207
+ notNull: true;
208
+ hasDefault: true;
209
+ isPrimaryKey: false;
210
+ isAutoincrement: false;
211
+ hasRuntimeDefault: false;
212
+ enumValues: undefined;
213
+ baseColumn: never;
214
+ identity: undefined;
215
+ generated: undefined;
216
+ }, {}, {}>;
217
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
218
+ name: "updated_at";
219
+ tableName: "user_identities";
220
+ dataType: "date";
221
+ columnType: "PgTimestamp";
222
+ data: Date;
223
+ driverParam: string;
224
+ notNull: true;
225
+ hasDefault: true;
226
+ isPrimaryKey: false;
227
+ isAutoincrement: false;
228
+ hasRuntimeDefault: false;
229
+ enumValues: undefined;
230
+ baseColumn: never;
231
+ identity: undefined;
232
+ generated: undefined;
233
+ }, {}, {}>;
234
+ };
235
+ dialect: "pg";
236
+ }>;
237
+ //# sourceMappingURL=user-identities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-identities.d.ts","sourceRoot":"","sources":["../../../src/pg/schema/user-identities.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuB1B,CAAC"}