@intx/db 0.1.2

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 (91) hide show
  1. package/README.md +37 -0
  2. package/drizzle.config.ts +23 -0
  3. package/migrations/.gitkeep +0 -0
  4. package/migrations/0000_brown_wither.sql +50 -0
  5. package/migrations/0001_white_aqueduct.sql +105 -0
  6. package/migrations/0002_clever_falcon.sql +56 -0
  7. package/migrations/0003_stiff_tyrannus.sql +44 -0
  8. package/migrations/0004_rename_capability_to_offering.sql +1 -0
  9. package/migrations/0005_gigantic_cardiac.sql +1 -0
  10. package/migrations/0006_sidecar.sql +8 -0
  11. package/migrations/0007_agent_running_session.sql +1 -0
  12. package/migrations/0008_session.sql +10 -0
  13. package/migrations/0009_session_messages.sql +18 -0
  14. package/migrations/0010_agent_sidecar_pubkey.sql +2 -0
  15. package/migrations/0011_session_message_from.sql +2 -0
  16. package/migrations/0012_agent_instance.sql +22 -0
  17. package/migrations/0013_instance_session_id.sql +2 -0
  18. package/migrations/0014_drop_agent_runtime_columns.sql +12 -0
  19. package/migrations/0015_add_instance_id_to_session_message.sql +2 -0
  20. package/migrations/0016_jazzy_gamma_corps.sql +3 -0
  21. package/migrations/0017_hesitant_marvex.sql +1 -0
  22. package/migrations/0018_natural_sinister_six.sql +5 -0
  23. package/migrations/0019_rename_grant_source_to_origin.sql +1 -0
  24. package/migrations/0020_add_agent_role.sql +9 -0
  25. package/migrations/0021_acoustic_ozymandias.sql +15 -0
  26. package/migrations/0022_material_sleepwalker.sql +27 -0
  27. package/migrations/0023_flawless_scarlet_witch.sql +2 -0
  28. package/migrations/0024_bumpy_sharon_ventura.sql +1 -0
  29. package/migrations/0025_curvy_firestar.sql +26 -0
  30. package/migrations/0026_keen_ultimo.sql +13 -0
  31. package/migrations/0027_git_tokens.sql +21 -0
  32. package/migrations/0028_wet_sugar_man.sql +1 -0
  33. package/migrations/meta/0000_snapshot.json +316 -0
  34. package/migrations/meta/0001_snapshot.json +968 -0
  35. package/migrations/meta/0002_snapshot.json +1315 -0
  36. package/migrations/meta/0003_snapshot.json +1594 -0
  37. package/migrations/meta/0004_snapshot.json +1594 -0
  38. package/migrations/meta/0005_snapshot.json +1600 -0
  39. package/migrations/meta/0011_snapshot.json +1921 -0
  40. package/migrations/meta/0012_snapshot.json +2067 -0
  41. package/migrations/meta/0013_snapshot.json +2082 -0
  42. package/migrations/meta/0014_snapshot.json +2049 -0
  43. package/migrations/meta/0015_snapshot.json +2064 -0
  44. package/migrations/meta/0016_snapshot.json +2085 -0
  45. package/migrations/meta/0017_snapshot.json +2085 -0
  46. package/migrations/meta/0018_snapshot.json +2070 -0
  47. package/migrations/meta/0019_snapshot.json +2070 -0
  48. package/migrations/meta/0020_snapshot.json +2126 -0
  49. package/migrations/meta/0021_snapshot.json +2239 -0
  50. package/migrations/meta/0022_snapshot.json +2425 -0
  51. package/migrations/meta/0023_snapshot.json +2260 -0
  52. package/migrations/meta/0024_snapshot.json +2254 -0
  53. package/migrations/meta/0025_snapshot.json +2418 -0
  54. package/migrations/meta/0026_snapshot.json +2508 -0
  55. package/migrations/meta/0027_snapshot.json +2657 -0
  56. package/migrations/meta/0028_snapshot.json +2657 -0
  57. package/migrations/meta/_journal.json +209 -0
  58. package/package.json +27 -0
  59. package/src/client.ts +24 -0
  60. package/src/config.ts +19 -0
  61. package/src/connection.ts +27 -0
  62. package/src/credential-resolution.ts +378 -0
  63. package/src/grant-store.ts +51 -0
  64. package/src/index.ts +32 -0
  65. package/src/migrate.test.ts +35 -0
  66. package/src/migrate.ts +168 -0
  67. package/src/parse-row.test.ts +113 -0
  68. package/src/parse-row.ts +185 -0
  69. package/src/schema/agent-assets.ts +21 -0
  70. package/src/schema/agents.ts +49 -0
  71. package/src/schema/assets.ts +24 -0
  72. package/src/schema/auth.ts +51 -0
  73. package/src/schema/credentials.ts +43 -0
  74. package/src/schema/git-tokens.ts +83 -0
  75. package/src/schema/grants.ts +26 -0
  76. package/src/schema/index.ts +19 -0
  77. package/src/schema/instances.ts +36 -0
  78. package/src/schema/messages.ts +108 -0
  79. package/src/schema/oauth-clients.ts +26 -0
  80. package/src/schema/offerings.ts +20 -0
  81. package/src/schema/principals.ts +28 -0
  82. package/src/schema/providers.ts +23 -0
  83. package/src/schema/roles.ts +51 -0
  84. package/src/schema/session-assets.ts +49 -0
  85. package/src/schema/sessions.ts +26 -0
  86. package/src/schema/sidecar.ts +14 -0
  87. package/src/schema/tenants.ts +41 -0
  88. package/src/schema/wallets.ts +44 -0
  89. package/src/tenant-hierarchy.ts +34 -0
  90. package/tsconfig.json +4 -0
  91. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,51 @@
1
+ import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ export const user = pgTable("user", {
4
+ id: text("id").primaryKey(),
5
+ name: text("name").notNull(),
6
+ email: text("email").notNull().unique(),
7
+ emailVerified: boolean("email_verified").notNull().default(false),
8
+ image: text("image"),
9
+ createdAt: timestamp("created_at").notNull().defaultNow(),
10
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
11
+ });
12
+
13
+ export const session = pgTable("session", {
14
+ id: text("id").primaryKey(),
15
+ userId: text("user_id")
16
+ .notNull()
17
+ .references(() => user.id, { onDelete: "cascade" }),
18
+ token: text("token").notNull().unique(),
19
+ expiresAt: timestamp("expires_at").notNull(),
20
+ ipAddress: text("ip_address"),
21
+ userAgent: text("user_agent"),
22
+ createdAt: timestamp("created_at").notNull().defaultNow(),
23
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
24
+ });
25
+
26
+ export const account = pgTable("account", {
27
+ id: text("id").primaryKey(),
28
+ userId: text("user_id")
29
+ .notNull()
30
+ .references(() => user.id, { onDelete: "cascade" }),
31
+ accountId: text("account_id").notNull(),
32
+ providerId: text("provider_id").notNull(),
33
+ accessToken: text("access_token"),
34
+ refreshToken: text("refresh_token"),
35
+ accessTokenExpiresAt: timestamp("access_token_expires_at"),
36
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
37
+ scope: text("scope"),
38
+ idToken: text("id_token"),
39
+ password: text("password"),
40
+ createdAt: timestamp("created_at").notNull().defaultNow(),
41
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
42
+ });
43
+
44
+ export const verification = pgTable("verification", {
45
+ id: text("id").primaryKey(),
46
+ identifier: text("identifier").notNull(),
47
+ value: text("value").notNull(),
48
+ expiresAt: timestamp("expires_at").notNull(),
49
+ createdAt: timestamp("created_at").notNull().defaultNow(),
50
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
51
+ });
@@ -0,0 +1,43 @@
1
+ import { jsonb, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";
2
+
3
+ import { oauthClient } from "./oauth-clients";
4
+ import { principal } from "./principals";
5
+ import { provider } from "./providers";
6
+ import { tenant } from "./tenants";
7
+
8
+ export const credential = pgTable(
9
+ "credential",
10
+ {
11
+ id: text("id").primaryKey(),
12
+ tenantId: text("tenant_id")
13
+ .notNull()
14
+ .references(() => tenant.id, { onDelete: "cascade" }),
15
+ principalId: text("principal_id").references(() => principal.id, {
16
+ onDelete: "set null",
17
+ }),
18
+ providerId: text("provider_id")
19
+ .notNull()
20
+ .references(() => provider.id, { onDelete: "cascade" }),
21
+ oauthClientId: text("oauth_client_id").references(() => oauthClient.id, {
22
+ onDelete: "set null",
23
+ }),
24
+ name: text("name").notNull(),
25
+ type: text("type", {
26
+ enum: ["api_key", "oauth_token", "certificate", "other"],
27
+ }).notNull(),
28
+ description: text("description"),
29
+ secret: text("secret").notNull(),
30
+ refreshSecret: text("refresh_secret"),
31
+ scopes: text("scopes").array(),
32
+ expiresAt: timestamp("expires_at"),
33
+ status: text("status", {
34
+ enum: ["active", "expired", "revoked", "error"],
35
+ })
36
+ .notNull()
37
+ .default("active"),
38
+ metadata: jsonb("metadata"),
39
+ createdAt: timestamp("created_at").notNull().defaultNow(),
40
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
41
+ },
42
+ (t) => [unique("credential_tenant_name").on(t.tenantId, t.name)],
43
+ );
@@ -0,0 +1,83 @@
1
+ import { sql } from "drizzle-orm";
2
+ import {
3
+ customType,
4
+ pgTable,
5
+ text,
6
+ timestamp,
7
+ uniqueIndex,
8
+ } from "drizzle-orm/pg-core";
9
+
10
+ import { user } from "./auth";
11
+ import { principal } from "./principals";
12
+ import { tenant } from "./tenants";
13
+
14
+ const bytea = customType<{ data: Uint8Array; driverData: Buffer }>({
15
+ dataType() {
16
+ return "bytea";
17
+ },
18
+ toDriver(value) {
19
+ return Buffer.from(value);
20
+ },
21
+ fromDriver(value) {
22
+ return new Uint8Array(value);
23
+ },
24
+ });
25
+
26
+ // Hub-issued bearer tokens for the smart-HTTP git endpoints. Opaque
27
+ // tokens of the form `itx_pat_<base64>` or `itx_svc_<base64>` whose
28
+ // SHA-256 digest is stored as `tokenHashSha256` (the raw secret is
29
+ // never persisted). Claims that bound the token's authority are
30
+ // modeled as typed columns rather than JSONB so the lookup path can
31
+ // rely on the database to enforce shape.
32
+ //
33
+ // `userId` identifies the owning user and is always set. `principalId`
34
+ // is set for tenant-bound tokens (the user acting in a specific
35
+ // tenant) and null for personal tokens that are not scoped to a
36
+ // principal. `tenantId` is always set for `kind: "svc"` tokens (they
37
+ // are inherently tenant-bound); for `kind: "pat"` it is set only when
38
+ // the user elected a tenant restriction at mint time. `kind`
39
+ // distinguishes interactive personal access tokens (`pat`) from
40
+ // service tokens (`svc`).
41
+ //
42
+ // `actions` stores the canonical RepoStore action vocabulary
43
+ // (`receivePack`, `createPack`, `resolveRef`, ...). The mint API
44
+ // translates user-facing aliases to canonical names before insert.
45
+ // `resource` is the single substrate authz resource string
46
+ // (e.g. `agent-state:ins_xxx`, `asset:def_yyy`) that the token grants
47
+ // access to. `refPattern` is a glob restricting which refs within the
48
+ // resource the token may read or write.
49
+ //
50
+ // Revocation is soft: setting `revokedAt` prevents future use while
51
+ // preserving the row for audit. The partial unique index on
52
+ // `(user_id, name)` filtered by `revoked_at is null` lets a user
53
+ // reuse a friendly name (e.g. "laptop") after revoking the old
54
+ // token bearing that name.
55
+ export const gitToken = pgTable(
56
+ "git_token",
57
+ {
58
+ id: text("id").primaryKey(),
59
+ tenantId: text("tenant_id").references(() => tenant.id),
60
+ userId: text("user_id")
61
+ .notNull()
62
+ .references(() => user.id, { onDelete: "cascade" }),
63
+ principalId: text("principal_id").references(() => principal.id, {
64
+ onDelete: "cascade",
65
+ }),
66
+ name: text("name").notNull(),
67
+ kind: text("kind", { enum: ["pat", "svc"] }).notNull(),
68
+ tokenHashSha256: bytea("token_hash_sha256").notNull().unique(),
69
+ resource: text("resource").notNull(),
70
+ refPattern: text("ref_pattern").notNull(),
71
+ actions: text("actions").array().notNull(),
72
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
73
+ revokedAt: timestamp("revoked_at", { withTimezone: true }),
74
+ createdAt: timestamp("created_at", { withTimezone: true })
75
+ .notNull()
76
+ .defaultNow(),
77
+ },
78
+ (t) => [
79
+ uniqueIndex("git_token_user_id_name_active_idx")
80
+ .on(t.userId, t.name)
81
+ .where(sql`${t.revokedAt} is null`),
82
+ ],
83
+ );
@@ -0,0 +1,26 @@
1
+ import { jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ import { principal } from "./principals";
4
+ import { role } from "./roles";
5
+ import { tenant } from "./tenants";
6
+
7
+ export const grant = pgTable("grant", {
8
+ id: text("id").primaryKey(),
9
+ tenantId: text("tenant_id")
10
+ .notNull()
11
+ .references(() => tenant.id, { onDelete: "cascade" }),
12
+ roleId: text("role_id").references(() => role.id, { onDelete: "cascade" }),
13
+ principalId: text("principal_id").references(() => principal.id, {
14
+ onDelete: "cascade",
15
+ }),
16
+ resource: text("resource").notNull(),
17
+ action: text("action").notNull(),
18
+ effect: text("effect", { enum: ["allow", "deny", "ask"] }).notNull(),
19
+ conditions: jsonb("conditions"),
20
+ origin: text("origin", {
21
+ enum: ["system", "role", "creator", "invoker"],
22
+ }).notNull(),
23
+ expiresAt: timestamp("expires_at"),
24
+ createdAt: timestamp("created_at").notNull().defaultNow(),
25
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
26
+ });
@@ -0,0 +1,19 @@
1
+ export * from "./auth";
2
+ export * from "./tenants";
3
+ export * from "./principals";
4
+ export * from "./roles";
5
+ export * from "./grants";
6
+ export * from "./agents";
7
+ export * from "./providers";
8
+ export * from "./oauth-clients";
9
+ export * from "./credentials";
10
+ export * from "./assets";
11
+ export * from "./agent-assets";
12
+ export * from "./wallets";
13
+ export * from "./offerings";
14
+ export * from "./sidecar";
15
+ export * from "./sessions";
16
+ export * from "./instances";
17
+ export * from "./session-assets";
18
+ export * from "./messages";
19
+ export * from "./git-tokens";
@@ -0,0 +1,36 @@
1
+ import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ import { agent, agentVersion } from "./agents";
4
+ import { agentSession } from "./sessions";
5
+ import { principal } from "./principals";
6
+ import { sidecar } from "./sidecar";
7
+ import { tenant } from "./tenants";
8
+
9
+ export const agentInstance = pgTable("agent_instance", {
10
+ id: text("id").primaryKey(),
11
+ agentId: text("agent_id")
12
+ .notNull()
13
+ .references(() => agent.id, { onDelete: "cascade" }),
14
+ tenantId: text("tenant_id")
15
+ .notNull()
16
+ .references(() => tenant.id, { onDelete: "cascade" }),
17
+ principalId: text("principal_id")
18
+ .notNull()
19
+ .references(() => principal.id),
20
+ address: text("address").notNull().unique(),
21
+ versionId: text("version_id").references(() => agentVersion.id),
22
+ sessionId: text("session_id").references(() => agentSession.id),
23
+ status: text("status", {
24
+ enum: ["deployed", "running", "updating", "error", "stopped"],
25
+ })
26
+ .notNull()
27
+ .default("deployed"),
28
+ sidecarId: text("sidecar_id").references(() => sidecar.id, {
29
+ onDelete: "set null",
30
+ }),
31
+ publicKey: text("public_key"),
32
+ kernelId: text("kernel_id"),
33
+ createdAt: timestamp("created_at").notNull().defaultNow(),
34
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
35
+ endedAt: timestamp("ended_at"),
36
+ });
@@ -0,0 +1,108 @@
1
+ import {
2
+ customType,
3
+ index,
4
+ integer,
5
+ jsonb,
6
+ pgTable,
7
+ text,
8
+ timestamp,
9
+ } from "drizzle-orm/pg-core";
10
+
11
+ import { agentInstance } from "./instances";
12
+ import { agentSession } from "./sessions";
13
+ import { tenant } from "./tenants";
14
+
15
+ const bytea = customType<{ data: Uint8Array; driverData: Buffer }>({
16
+ dataType() {
17
+ return "bytea";
18
+ },
19
+ toDriver(value) {
20
+ return Buffer.from(value);
21
+ },
22
+ fromDriver(value) {
23
+ return new Uint8Array(value);
24
+ },
25
+ });
26
+
27
+ export const inferenceTurn = pgTable(
28
+ "inference_turn",
29
+ {
30
+ id: text("id").primaryKey(),
31
+ sessionId: text("session_id")
32
+ .notNull()
33
+ .references(() => agentSession.id, { onDelete: "cascade" }),
34
+ instanceId: text("instance_id")
35
+ .notNull()
36
+ .references(() => agentInstance.id, { onDelete: "cascade" }),
37
+ tenantId: text("tenant_id")
38
+ .notNull()
39
+ .references(() => tenant.id, { onDelete: "cascade" }),
40
+ model: text("model").notNull(),
41
+ status: text("status", { enum: ["running", "completed", "failed"] })
42
+ .notNull()
43
+ .default("running"),
44
+ startedAt: timestamp("started_at").notNull(),
45
+ endedAt: timestamp("ended_at"),
46
+ },
47
+ (t) => [
48
+ index("inference_turn_instance_id_started_at_idx").on(
49
+ t.instanceId,
50
+ t.startedAt,
51
+ ),
52
+ ],
53
+ );
54
+
55
+ export const turnPart = pgTable("turn_part", {
56
+ id: text("id").primaryKey(),
57
+ turnId: text("turn_id")
58
+ .notNull()
59
+ .references(() => inferenceTurn.id, { onDelete: "cascade" }),
60
+ sessionId: text("session_id")
61
+ .notNull()
62
+ .references(() => agentSession.id, { onDelete: "cascade" }),
63
+ type: text("type", {
64
+ enum: [
65
+ "text",
66
+ "reasoning",
67
+ "tool",
68
+ "file",
69
+ "error",
70
+ "refusal",
71
+ "step-start",
72
+ "step-finish",
73
+ "snapshot",
74
+ "patch",
75
+ ],
76
+ }).notNull(),
77
+ content: text("content"),
78
+ metadata: jsonb("metadata"),
79
+ ordinal: integer("ordinal").notNull(),
80
+ });
81
+
82
+ export const sessionMail = pgTable(
83
+ "session_mail",
84
+ {
85
+ id: text("id").primaryKey(),
86
+ sessionId: text("session_id")
87
+ .notNull()
88
+ .references(() => agentSession.id, { onDelete: "cascade" }),
89
+ instanceId: text("instance_id").references(() => agentInstance.id, {
90
+ onDelete: "set null",
91
+ }),
92
+ tenantId: text("tenant_id")
93
+ .notNull()
94
+ .references(() => tenant.id, { onDelete: "cascade" }),
95
+ direction: text("direction", { enum: ["inbound", "outbound"] }).notNull(),
96
+ status: text("status", { enum: ["pending", "delivered"] })
97
+ .notNull()
98
+ .default("pending"),
99
+ raw: bytea("raw").notNull(),
100
+ createdAt: timestamp("created_at").notNull().defaultNow(),
101
+ },
102
+ (t) => [
103
+ index("session_mail_instance_id_created_at_idx").on(
104
+ t.instanceId,
105
+ t.createdAt,
106
+ ),
107
+ ],
108
+ );
@@ -0,0 +1,26 @@
1
+ import { jsonb, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";
2
+
3
+ import { provider } from "./providers";
4
+ import { tenant } from "./tenants";
5
+
6
+ export const oauthClient = pgTable(
7
+ "oauth_client",
8
+ {
9
+ id: text("id").primaryKey(),
10
+ tenantId: text("tenant_id")
11
+ .notNull()
12
+ .references(() => tenant.id, { onDelete: "cascade" }),
13
+ providerId: text("provider_id")
14
+ .notNull()
15
+ .references(() => provider.id, { onDelete: "cascade" }),
16
+ name: text("name").notNull(),
17
+ clientId: text("client_id").notNull(),
18
+ clientSecret: text("client_secret").notNull(),
19
+ redirectUris: text("redirect_uris").array(),
20
+ defaultScopes: text("default_scopes").array(),
21
+ metadata: jsonb("metadata"),
22
+ createdAt: timestamp("created_at").notNull().defaultNow(),
23
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
24
+ },
25
+ (t) => [unique("oauth_client_tenant_provider").on(t.tenantId, t.providerId)],
26
+ );
@@ -0,0 +1,20 @@
1
+ import { jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ import { agent } from "./agents";
4
+ import { tenant } from "./tenants";
5
+
6
+ export const offering = pgTable("offering", {
7
+ id: text("id").primaryKey(),
8
+ agentId: text("agent_id")
9
+ .notNull()
10
+ .references(() => agent.id, { onDelete: "cascade" }),
11
+ tenantId: text("tenant_id")
12
+ .notNull()
13
+ .references(() => tenant.id, { onDelete: "cascade" }),
14
+ name: text("name").notNull(),
15
+ description: text("description"),
16
+ pricing: jsonb("pricing"),
17
+ schema: jsonb("schema"),
18
+ createdAt: timestamp("created_at").notNull().defaultNow(),
19
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
20
+ });
@@ -0,0 +1,28 @@
1
+ import { pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";
2
+
3
+ import { user } from "./auth";
4
+ import { tenant } from "./tenants";
5
+
6
+ export const principal = pgTable(
7
+ "principal",
8
+ {
9
+ id: text("id").primaryKey(),
10
+ tenantId: text("tenant_id")
11
+ .notNull()
12
+ .references(() => tenant.id, { onDelete: "cascade" }),
13
+ kind: text("kind", { enum: ["user", "agent"] }).notNull(),
14
+ refId: text("ref_id").notNull(),
15
+ status: text("status", {
16
+ enum: ["active", "suspended", "invited", "deactivated"],
17
+ }).notNull(),
18
+ createdAt: timestamp("created_at").notNull().defaultNow(),
19
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
20
+ },
21
+ (t) => [unique().on(t.tenantId, t.kind, t.refId)],
22
+ );
23
+
24
+ // The refId on a user principal points to the auth user table.
25
+ // We don't add a FK constraint because refId also points to agent.id
26
+ // when kind='agent', and Postgres can't do conditional FKs.
27
+ // The application layer enforces referential integrity.
28
+ export { user };
@@ -0,0 +1,23 @@
1
+ import { jsonb, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";
2
+
3
+ import { tenant } from "./tenants";
4
+
5
+ export const provider = pgTable(
6
+ "provider",
7
+ {
8
+ id: text("id").primaryKey(),
9
+ tenantId: text("tenant_id")
10
+ .notNull()
11
+ .references(() => tenant.id, { onDelete: "cascade" }),
12
+ name: text("name").notNull(),
13
+ plugin: text("plugin").notNull(),
14
+ authorizationUrl: text("authorization_url"),
15
+ tokenUrl: text("token_url"),
16
+ userInfoUrl: text("user_info_url"),
17
+ scopes: text("scopes").array(),
18
+ metadata: jsonb("metadata"),
19
+ createdAt: timestamp("created_at").notNull().defaultNow(),
20
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
21
+ },
22
+ (t) => [unique("provider_tenant_name").on(t.tenantId, t.name)],
23
+ );
@@ -0,0 +1,51 @@
1
+ import {
2
+ boolean,
3
+ pgTable,
4
+ primaryKey,
5
+ text,
6
+ timestamp,
7
+ } from "drizzle-orm/pg-core";
8
+
9
+ import { agent } from "./agents";
10
+ import { principal } from "./principals";
11
+ import { tenant } from "./tenants";
12
+
13
+ export const role = pgTable("role", {
14
+ id: text("id").primaryKey(),
15
+ tenantId: text("tenant_id")
16
+ .notNull()
17
+ .references(() => tenant.id, { onDelete: "cascade" }),
18
+ name: text("name").notNull(),
19
+ description: text("description"),
20
+ isSystem: boolean("is_system").notNull().default(false),
21
+ createdAt: timestamp("created_at").notNull().defaultNow(),
22
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
23
+ });
24
+
25
+ export const agentRole = pgTable(
26
+ "agent_role",
27
+ {
28
+ agentId: text("agent_id")
29
+ .notNull()
30
+ .references(() => agent.id, { onDelete: "cascade" }),
31
+ roleId: text("role_id")
32
+ .notNull()
33
+ .references(() => role.id, { onDelete: "cascade" }),
34
+ createdAt: timestamp("created_at").notNull().defaultNow(),
35
+ },
36
+ (t) => [primaryKey({ columns: [t.agentId, t.roleId] })],
37
+ );
38
+
39
+ export const principalRole = pgTable(
40
+ "principal_role",
41
+ {
42
+ principalId: text("principal_id")
43
+ .notNull()
44
+ .references(() => principal.id, { onDelete: "cascade" }),
45
+ roleId: text("role_id")
46
+ .notNull()
47
+ .references(() => role.id, { onDelete: "cascade" }),
48
+ createdAt: timestamp("created_at").notNull().defaultNow(),
49
+ },
50
+ (t) => [primaryKey({ columns: [t.principalId, t.roleId] })],
51
+ );
@@ -0,0 +1,49 @@
1
+ import {
2
+ index,
3
+ pgTable,
4
+ primaryKey,
5
+ text,
6
+ timestamp,
7
+ } from "drizzle-orm/pg-core";
8
+
9
+ import { agentAsset } from "./agent-assets";
10
+ import { agentInstance } from "./instances";
11
+
12
+ // session_asset rows record per-(instance, agent_asset) pack
13
+ // acknowledgments. A row exists iff the sidecar acked the pack that
14
+ // materialized that asset for that instance. The launchSession flow
15
+ // inserts each row before the corresponding pack send and rolls it
16
+ // back if that single send fails, so each row reflects an ack the
17
+ // hub actually observed for that attachment.
18
+ //
19
+ // Caveat on multi-attachment partial-success: when a session attaches
20
+ // N assets and the fan-out succeeds for attachments 1..k-1 and fails
21
+ // on attachment k, only attachment k's row is rolled back. Rows
22
+ // 1..k-1 stay. The session as a whole never reached the running
23
+ // state — attemptCleanup runs sendAgentUndeploy — but the per-
24
+ // attachment ack invariant holds for the rows that remain.
25
+ // Forensics queries should treat row count as "packs the sidecar
26
+ // acked during this instance's launch," not "sessions that ran with
27
+ // assets." Pairing with agent_instance.status (or its successor
28
+ // session-lifecycle signal) is necessary to distinguish a row left
29
+ // behind by a partially-successful failed launch from one belonging
30
+ // to a fully-running session.
31
+ export const sessionAsset = pgTable(
32
+ "session_asset",
33
+ {
34
+ instanceId: text("instance_id")
35
+ .notNull()
36
+ .references(() => agentInstance.id, { onDelete: "cascade" }),
37
+ agentAssetId: text("agent_asset_id")
38
+ .notNull()
39
+ .references(() => agentAsset.id, { onDelete: "cascade" }),
40
+ mountPath: text("mount_path").notNull(),
41
+ assetPackSha: text("asset_pack_sha").notNull(),
42
+ sourceCommitSha: text("source_commit_sha").notNull(),
43
+ materializedAt: timestamp("materialized_at").notNull().defaultNow(),
44
+ },
45
+ (t) => [
46
+ primaryKey({ columns: [t.instanceId, t.agentAssetId] }),
47
+ index("session_asset_pack_sha_idx").on(t.assetPackSha),
48
+ ],
49
+ );
@@ -0,0 +1,26 @@
1
+ import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ import { agent } from "./agents";
4
+ import { principal } from "./principals";
5
+ import { tenant } from "./tenants";
6
+
7
+ export const agentSession = pgTable("agent_session", {
8
+ id: text("id").primaryKey(),
9
+ tenantId: text("tenant_id")
10
+ .notNull()
11
+ .references(() => tenant.id, { onDelete: "cascade" }),
12
+ agentId: text("agent_id")
13
+ .notNull()
14
+ .references(() => agent.id, { onDelete: "cascade" }),
15
+ principalId: text("principal_id")
16
+ .notNull()
17
+ .references(() => principal.id),
18
+ status: text("status", {
19
+ enum: ["active", "ending", "ended"],
20
+ })
21
+ .notNull()
22
+ .default("active"),
23
+ createdAt: timestamp("created_at").notNull().defaultNow(),
24
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
25
+ endedAt: timestamp("ended_at"),
26
+ });
@@ -0,0 +1,14 @@
1
+ import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ export const sidecar = pgTable("sidecar", {
4
+ id: text("id").primaryKey(),
5
+ url: text("url").notNull(),
6
+ status: text("status", {
7
+ enum: ["online", "offline", "error"],
8
+ })
9
+ .notNull()
10
+ .default("online"),
11
+ lastHeartbeat: timestamp("last_heartbeat"),
12
+ createdAt: timestamp("created_at").notNull().defaultNow(),
13
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
14
+ });
@@ -0,0 +1,41 @@
1
+ import {
2
+ foreignKey,
3
+ jsonb,
4
+ pgTable,
5
+ text,
6
+ timestamp,
7
+ unique,
8
+ } from "drizzle-orm/pg-core";
9
+
10
+ export const tenant = pgTable(
11
+ "tenant",
12
+ {
13
+ id: text("id").primaryKey(),
14
+ name: text("name").notNull(),
15
+ slug: text("slug").notNull().unique(),
16
+ domain: text("domain").notNull().unique(),
17
+ parentId: text("parent_id"),
18
+ config: jsonb("config"),
19
+ createdAt: timestamp("created_at").notNull().defaultNow(),
20
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
21
+ },
22
+ (t) => [foreignKey({ columns: [t.parentId], foreignColumns: [t.id] })],
23
+ );
24
+
25
+ export const federationTrust = pgTable(
26
+ "federation_trust",
27
+ {
28
+ id: text("id").primaryKey(),
29
+ tenantId: text("tenant_id")
30
+ .notNull()
31
+ .references(() => tenant.id, { onDelete: "cascade" }),
32
+ targetTenantId: text("target_tenant_id")
33
+ .notNull()
34
+ .references(() => tenant.id, { onDelete: "cascade" }),
35
+ direction: text("direction", {
36
+ enum: ["inbound", "outbound", "bilateral"],
37
+ }).notNull(),
38
+ createdAt: timestamp("created_at").notNull().defaultNow(),
39
+ },
40
+ (t) => [unique().on(t.tenantId, t.targetTenantId)],
41
+ );